Merge lp:~unity-team/unity/infographics-with-lightdm into lp:unity/phablet

Proposed by Nicolas d'Offay
Status: Superseded
Proposed branch: lp:~unity-team/unity/infographics-with-lightdm
Merge into: lp:unity/phablet
Diff against target: 2968 lines (+2427/-107)
36 files modified
Greeter/Circle.qml (+61/-0)
Greeter/CirclePositioner.qml (+36/-0)
Greeter/Dot.qml (+36/-0)
Greeter/Greeter.qml (+6/-42)
Greeter/GreeterContent.qml (+17/-11)
Greeter/Infographics.desktop (+9/-0)
Greeter/Infographics.qml (+300/-0)
Greeter/Infographics.qmlproject (+20/-0)
Greeter/LoginList.qml (+45/-16)
Shell.qml (+7/-3)
debian/control (+0/-1)
debian/qml-phone-shell.install (+1/-0)
plugins/CMakeLists.txt (+1/-0)
plugins/LightDM/CMakeLists.txt (+49/-0)
plugins/LightDM/greeter.cpp (+133/-0)
plugins/LightDM/greeter.h (+70/-0)
plugins/LightDM/infographicmodel.cpp (+21/-0)
plugins/LightDM/infographicmodel.h (+26/-0)
plugins/LightDM/lightdm-greeter.cpp (+195/-0)
plugins/LightDM/lightdm-greeter.h (+105/-0)
plugins/LightDM/lightdm-infographicmodel.cpp (+370/-0)
plugins/LightDM/lightdm-infographicmodel.h (+95/-0)
plugins/LightDM/lightdm-usersmodel.cpp (+166/-0)
plugins/LightDM/lightdm-usersmodel.h (+66/-0)
plugins/LightDM/plugin.cpp (+57/-0)
plugins/LightDM/plugin.h (+35/-0)
plugins/LightDM/qmldir (+2/-0)
plugins/LightDM/qvariantlistmodel.cpp (+243/-0)
plugins/LightDM/qvariantlistmodel.h (+57/-0)
plugins/LightDM/usersmodel.cpp (+68/-0)
plugins/LightDM/usersmodel.h (+37/-0)
plugins/Utils/qsortfilterproxymodelqml.cpp (+10/-0)
plugins/Utils/qsortfilterproxymodelqml.h (+1/-0)
tests/autopilot/qml_phone_shell/tests/helpers.py (+36/-13)
tests/qmltests/CMakeLists.txt (+2/-2)
tests/qmltests/Greeter/tst_Greeter.qml (+44/-19)
To merge this branch: bzr merge lp:~unity-team/unity/infographics-with-lightdm
Reviewer Review Type Date Requested Status
Michael Zanetti Pending
MichaƂ Sawicz progress Pending
Review via email: mp+163302@code.launchpad.net

This proposal has been superseded by a proposal from 2013-05-10.

Commit message

Commit of infographics with initial animation.

Description of the change

This is an initial MP of the infographics with one animation. Currently everything is done via QML for the visuals however in future the circles will have to be rendered together via C++/GL. Any suggestions on what could be improved with the Infographics.qml would be much appreciated.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'Greeter/Circle.qml'
2--- Greeter/Circle.qml 1970-01-01 00:00:00 +0000
3+++ Greeter/Circle.qml 2013-05-10 12:09:35 +0000
4@@ -0,0 +1,61 @@
5+/*
6+ * Copyright (C) 2013 Canonical, Ltd.
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+import QtQuick 2.0
22+
23+Item {
24+ property color color
25+
26+ scale: 0.0
27+
28+ ShaderEffect {
29+ id: shader
30+
31+ property real size: Math.min(width, height) * 0.5
32+ property variant scale: Qt.size(width >= height ? width / height : 1.0,
33+ width >= height ? 1.0 : height / width)
34+ property color color: Qt.rgba(parent.color.r * parent.color.a,
35+ parent.color.g * parent.color.a,
36+ parent.color.b * parent.color.a,
37+ parent.color.a)
38+
39+ visible: size > 0.0
40+ anchors.fill: parent
41+ vertexShader: "
42+ uniform mediump mat4 qt_Matrix;
43+ uniform mediump vec2 scale;
44+ attribute mediump vec4 qt_Vertex;
45+ attribute mediump vec2 qt_MultiTexCoord0;
46+ varying highp vec2 coord;
47+ void main() {
48+ // Put coord in the range [-1, 1].
49+ coord = ((qt_MultiTexCoord0 * 2.0) - 1.0) * scale;
50+ gl_Position = qt_Matrix * qt_Vertex;
51+ }
52+ "
53+ fragmentShader: "
54+ varying highp vec2 coord;
55+ uniform mediump float qt_Opacity;
56+ uniform mediump vec4 color;
57+ uniform mediump float size;
58+ void main() {
59+ // Distance function for antialiased circle.
60+ mediump float value = clamp(size + 0.5 - size * length(coord), 0.0, 1.0);
61+ gl_FragColor = color.rgba * vec4(qt_Opacity * value);
62+ }
63+ "
64+ }
65+}
66
67=== added file 'Greeter/CirclePositioner.qml'
68--- Greeter/CirclePositioner.qml 1970-01-01 00:00:00 +0000
69+++ Greeter/CirclePositioner.qml 2013-05-10 12:09:35 +0000
70@@ -0,0 +1,36 @@
71+/*
72+ * Copyright (C) 2013 Canonical, Ltd.
73+ *
74+ * This program is free software; you can redistribute it and/or modify
75+ * it under the terms of the GNU General Public License as published by
76+ * the Free Software Foundation; version 3.
77+ *
78+ * This program is distributed in the hope that it will be useful,
79+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
80+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
81+ * GNU General Public License for more details.
82+ *
83+ * You should have received a copy of the GNU General Public License
84+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
85+ */
86+
87+import QtQuick 2.0
88+import Ubuntu.Components 0.1
89+
90+Item {
91+ property int count
92+ property int index
93+ property real radius
94+ property real halfSize
95+ property real posOffset
96+
97+ property real slice: (2 * Math.PI / count) * index;
98+
99+ implicitWidth: childrenRect.width
100+ implicitHeight: childrenRect.height
101+
102+ x: (radius - halfSize * posOffset) * Math.sin(slice) + radius - halfSize
103+ y: (radius - halfSize * posOffset) * -Math.cos(slice) + radius - halfSize
104+
105+ rotation: Math.atan2(radius-(y+halfSize), radius-(x+halfSize)) * 180 / Math.PI - 90
106+}
107
108=== added file 'Greeter/Dot.qml'
109--- Greeter/Dot.qml 1970-01-01 00:00:00 +0000
110+++ Greeter/Dot.qml 2013-05-10 12:09:35 +0000
111@@ -0,0 +1,36 @@
112+/*
113+ * Copyright (C) 2013 Canonical, Ltd.
114+ *
115+ * This program is free software; you can redistribute it and/or modify
116+ * it under the terms of the GNU General Public License as published by
117+ * the Free Software Foundation; version 3.
118+ *
119+ * This program is distributed in the hope that it will be useful,
120+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
121+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
122+ * GNU General Public License for more details.
123+ *
124+ * You should have received a copy of the GNU General Public License
125+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
126+ */
127+
128+import QtQuick 2.0
129+
130+Image {
131+states: [
132+ State {
133+ name: "unfilled"
134+ PropertyChanges { target: dot; source: "graphics/dot_empty@30.png" }
135+ },
136+
137+ State {
138+ name: "filled"
139+ PropertyChanges { target: dot; source: "graphics/dot_filled@30.png" }
140+ },
141+
142+ State {
143+ name: "pointer"
144+ PropertyChanges { target: dot; source: "graphics/dot_pointer@30.png" }
145+ }
146+]
147+}
148
149=== modified file 'Greeter/Greeter.qml'
150--- Greeter/Greeter.qml 2013-04-09 00:39:33 +0000
151+++ Greeter/Greeter.qml 2013-05-10 12:09:35 +0000
152@@ -16,7 +16,7 @@
153
154 import QtQuick 2.0
155 import Ubuntu.Components 0.1
156-import QtQuick.XmlListModel 2.0
157+import LightDM 0.1 as LightDM
158 import "../Components"
159
160 Showable {
161@@ -24,8 +24,8 @@
162 enabled: shown
163 created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready
164
165- property alias model: userList
166- property bool locked: shown && multiUser && !greeterContentLoader.guestAccount
167+ property alias model: greeterContentLoader.model
168+ property bool locked: shown && multiUser && !greeterContentLoader.promptless
169
170 readonly property bool narrowMode: width <= units.gu(60)
171 readonly property bool multiUser: !narrowMode // TODO: populate with real value
172@@ -33,50 +33,14 @@
173 signal selected(int uid)
174 signal unlocked(int uid)
175
176- ListModel {
177- id: userList
178- objectName: "userList"
179- ListElement { name: "Guest"; background: "graphics/tablet_background.jpg" }
180- }
181-
182- // TODO: Replace with a ModelAggregator once we have one.
183- // Check Unity2D for a possibly usable one.
184- XmlListModel {
185- id: xmlReader
186- source: "/usr/share/demo-assets/shell/users.xml"
187- query: "/users/user"
188-
189- XmlRole { name: "name"; query: "username/string()" }
190- XmlRole { name: "password"; query: "password/string()" }
191- XmlRole { name: "background"; query: "background/string()" }
192- XmlRole { name: "infographic"; query: "infographic/string()" }
193-
194- onStatusChanged: {
195- if (status == XmlListModel.Ready) {
196- // Need to reset the model to make sure everything updates correctly.
197- // Otherwise currentIndex, count etc could stay the same and bindings are not updated.
198- greeterContentLoader.currentIndex = -1;
199- greeterContentLoader.model = undefined;
200-
201- userList.clear();
202- for (var i = 0; i < xmlReader.count; i++) {
203- userList.append(xmlReader.get(i));
204- }
205- userList.append({ "name": "Guest", background: "graphics/tablet_background.jpg" });
206-
207- greeterContentLoader.model = userList;
208- greeterContentLoader.currentIndex = 0;
209- }
210- }
211- }
212-
213 Loader {
214 id: greeterContentLoader
215 objectName: "greeterContentLoader"
216 anchors.fill: parent
217- property variant model: userList
218+ property variant model: LightDM.Users
219 property int currentIndex: 0
220- property bool guestAccount: item ? item.guestAccount : false
221+ property bool promptless: item ? item.promptless : false
222+ property variant infographicModel: LightDM.Infographic
223
224 source: required ? "GreeterContent.qml" : ""
225
226
227=== modified file 'Greeter/GreeterContent.qml'
228--- Greeter/GreeterContent.qml 2013-04-05 11:03:11 +0000
229+++ Greeter/GreeterContent.qml 2013-05-10 12:09:35 +0000
230@@ -16,13 +16,14 @@
231
232 import QtQuick 2.0
233 import Ubuntu.Components 0.1
234+import LightDM 0.1 as LightDM
235 import "../Components"
236
237 MouseArea {
238 id: root
239 anchors.fill: parent
240
241- property bool guestAccount: loginLoader.status == Loader.Ready && loginLoader.item.guestAccount
242+ property bool promptless: loginLoader.status == Loader.Ready && LightDM.Greeter.promptless
243 property bool ready: wallpaper.status == Image.Ready
244
245 signal selected(int uid)
246@@ -69,6 +70,8 @@
247
248 onLoaded: {
249 item.currentIndex = greeterContentLoader.currentIndex
250+ greeterContentLoader.infographicModel.username = greeterContentLoader.model.data(
251+ greeterContentLoader.currentIndex, LightDM.UserRoles.NameRole)
252 }
253
254 Binding {
255@@ -90,25 +93,28 @@
256
257 onCurrentIndexChanged: {
258 greeterContentLoader.currentIndex = loginLoader.item.currentIndex;
259+ greeterContentLoader.infographicModel.username = greeterContentLoader.model.data(
260+ greeterContentLoader.currentIndex, LightDM.UserRoles.NameRole)
261 }
262 }
263 }
264
265- // FIXME: The images are way bigger than their content because the infographic
266- // bubbles can grow a lot. Because of that the infographic image reaches under the
267- // login list. This code makes sure that it never really overlaps until unusable
268- // with the current artwork. Will be replaced by real infographic.
269- CrossFadeImage {
270+ Infographics {
271 id: infographics
272- width: narrowMode ? sourceSize.width : Math.min(parent.width - loginLoader.x - loginLoader.width/2, units.gu(90))
273- height: narrowMode ? sourceSize.height : width * 8/9
274+ width: narrowMode ? parent.width : parent.width / 1.7
275+ height: narrowMode ? parent.height : width / 1.7
276+
277+ Component.onCompleted: {
278+ greeterContentLoader.infographicModel.username = "phablet"
279+ }
280+
281+ model: greeterContentLoader.infographicModel
282
283 anchors {
284 verticalCenter: parent.verticalCenter
285 horizontalCenter: narrowMode ? parent.horizontalCenter : undefined
286- right: narrowMode ? undefined : parent.right
287- rightMargin: narrowMode ? 0 : units.gu(-6)
288+ left: narrowMode ? undefined : loginLoader.right
289+ leftMargin: narrowMode ? 0 : width / 6
290 }
291- source: narrowMode ? "graphics/infographic_tweeter_cut.png" : userList.get(greeterContentLoader.currentIndex).infographic ? userList.get(greeterContentLoader.currentIndex).infographic : ""
292 }
293 }
294
295=== added file 'Greeter/Infographics.desktop'
296--- Greeter/Infographics.desktop 1970-01-01 00:00:00 +0000
297+++ Greeter/Infographics.desktop 2013-05-10 12:09:35 +0000
298@@ -0,0 +1,9 @@
299+[Desktop Entry]
300+Name=Infographics
301+Comment=My project description
302+Exec=/usr/bin/qmlscene $@ /usr/share/Infographics/Infographics.qml
303+Icon=qmlscene
304+Terminal=false
305+Type=Application
306+X-Ubuntu-Touch=true
307+Name[en_GB]=Infographics.desktop
308
309=== added file 'Greeter/Infographics.qml'
310--- Greeter/Infographics.qml 1970-01-01 00:00:00 +0000
311+++ Greeter/Infographics.qml 2013-05-10 12:09:35 +0000
312@@ -0,0 +1,300 @@
313+/*
314+ * Copyright (C) 2013 Canonical, Ltd.
315+ *
316+ * This program is free software; you can redistribute it and/or modify
317+ * it under the terms of the GNU General Public License as published by
318+ * the Free Software Foundation; version 3.
319+ *
320+ * This program is distributed in the hope that it will be useful,
321+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
322+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
323+ * GNU General Public License for more details.
324+ *
325+ * You should have received a copy of the GNU General Public License
326+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
327+ */
328+
329+import QtQuick 2.0
330+import Ubuntu.Components 0.1
331+
332+Rectangle {
333+ id: infographic
334+
335+ property int animDuration: 20
336+
337+ Component.onCompleted: {
338+ dotAnimTimer.start();
339+ notification.showAnim.start();
340+ }
341+
342+ color: "transparent"
343+
344+ property variant model
345+
346+ Connections {
347+ target: model
348+
349+ onLabelChanged: {
350+ dotAnimTimer.dotCounter = 0;
351+ circleAnimTimer.pastCircleCounter = 0;
352+ circleAnimTimer.presentCircleCounter = 0;
353+ dotAnimTimer.start();
354+ notification.showAnim.start();
355+ }
356+ }
357+
358+ visible: model.label !== ""
359+
360+ Item {
361+ id: dataCircle
362+
363+ width: Math.min(parent.height, parent.width) / 1.5
364+ height: width
365+
366+ anchors.centerIn: parent
367+
368+ Timer {
369+ id: circleAnimTimer
370+
371+ property int pastCircleCounter: 0
372+ property int presentCircleCounter: 0
373+
374+ interval: infographic.animDuration; running: false; repeat: true
375+ onTriggered: {
376+ if(presentCircles.count === 0 || pastCircles.count === 0) {
377+ stop()
378+ pastCircleCounter = presentCircleCounter = 0
379+ }
380+
381+ if (pastCircleCounter < pastCircles.count) {
382+ var nextCircle = pastCircles.itemAt(pastCircleCounter++)
383+ nextCircle.pastUnlockAnim.start()
384+ }
385+ if (pastCircleCounter >= pastCircles.count / 2) {
386+ var nextCircle = presentCircles.itemAt(presentCircleCounter++)
387+ nextCircle.presentUnlockAnim.start()
388+ }
389+ if (presentCircleCounter >= presentCircles.count) {
390+ stop()
391+ pastCircleCounter = presentCircleCounter = 0
392+ }
393+ }
394+ }
395+
396+ Repeater {
397+ id: pastCircles
398+
399+ model: infographic.model.secondMonth
400+
401+ delegate: CirclePositioner {
402+ index: model.index
403+ count: pastCircles.count
404+ radius: parent.width / 2
405+ halfSize: pastCircle.width / 2
406+ posOffset: 0.0
407+ visible: modelData !== null
408+
409+ property var pastUnlockAnim: pastCircleUnlockAnim
410+
411+ Circle {
412+ id: pastCircle
413+
414+ width: dataCircle.width / 2
415+ height: dataCircle.height / 2
416+
417+ Component.onCompleted: {
418+ color = infographic.model.secondColor
419+ }
420+
421+ SequentialAnimation {
422+ id: pastCircleUnlockAnim
423+
424+ loops: 1
425+ ParallelAnimation {
426+ PropertyAnimation {
427+ target: pastCircle
428+ property: "opacity"
429+ from: 0.0
430+ to: 0.1
431+ easing.type: Easing.OutCurve
432+ duration: circleAnimTimer.interval * 4
433+ }
434+ PropertyAnimation {
435+ target: pastCircle
436+ property: "scale"
437+ to: 0.6 + modelData
438+ easing.type: Easing.OutCurve
439+ duration: circleAnimTimer.interval * 4
440+ }
441+ }
442+ }
443+ }
444+ }
445+ }
446+
447+ Repeater {
448+ id: presentCircles
449+
450+ model: infographic.model.firstMonth
451+
452+ delegate: CirclePositioner {
453+ index: model.index
454+ count: presentCircles.count
455+ radius: parent.width / 2
456+ halfSize: presentCircle.width / 2
457+ posOffset: 0.0
458+ visible: modelData !== null
459+
460+ property var presentUnlockAnim: presentCircleUnlockAnim
461+
462+ Circle {
463+ id: presentCircle
464+
465+ width: dataCircle.width / 2
466+ height: dataCircle.height / 2
467+
468+ Component.onCompleted: {
469+ color = infographic.model.firstColor
470+ }
471+
472+ SequentialAnimation {
473+ id: presentCircleUnlockAnim
474+
475+ loops: 1
476+
477+ ParallelAnimation {
478+ PropertyAnimation {
479+ target: presentCircle
480+ property: "opacity"
481+ from: 0.0
482+ to: 0.3
483+ easing.type: Easing.OutCurve
484+ duration: circleAnimTimer.interval * 4
485+ }
486+ PropertyAnimation {
487+ target: presentCircle
488+ property: "scale"
489+ from: 0.0
490+ to: modelData
491+ easing.type: Easing.OutCurve
492+ duration: circleAnimTimer.interval * 4
493+ }
494+ }
495+ }
496+ }
497+ }
498+ }
499+
500+ Image {
501+ id: backgroundCircle
502+
503+ anchors.fill: parent
504+ opacity: 0.9
505+
506+ source: "graphics/infographic_circle.png"
507+ }
508+
509+ Timer {
510+ id: dotAnimTimer
511+
512+ property int dotCounter: 0
513+
514+ interval: animDuration * 0.5; running: false; repeat: true
515+ onTriggered: {
516+ if (dotCounter < dots.count) {
517+ var nextDot = dots.itemAt(dotCounter++);
518+ nextDot.unlockAnimation.start()
519+ }
520+ else {
521+ stop()
522+ }
523+ if (dotCounter >= dots.count / 2) {
524+ circleAnimTimer.start()
525+ }
526+ }
527+ }
528+
529+ Repeater {
530+ id: dots
531+
532+ model: infographic.model.firstMonth
533+
534+ delegate: CirclePositioner {
535+ index: model.index
536+ count: dots.count
537+ radius: backgroundCircle.width / 2
538+ halfSize: dot.width / 2
539+ posOffset: radius / 180
540+ state: dot.state
541+
542+ property var unlockAnimation: dotUnlockAnim
543+
544+ property int currentDay: infographic.model.currentDay
545+
546+ Dot {
547+ id: dot
548+
549+ visible: false
550+ smooth: true
551+ scale: radius / 1000
552+ state: {
553+ if (index < currentDay)
554+ "filled"
555+ else if (index == currentDay)
556+ "pointer"
557+ else
558+ "unfilled"
559+ }
560+
561+ SequentialAnimation {
562+ id: dotUnlockAnim
563+
564+ PropertyAnimation {
565+ target: dot
566+ property: "visible"
567+ to: "true"
568+ duration: dotAnimTimer.interval / 2
569+ }
570+ }
571+ }
572+ }
573+ }
574+
575+ Text {
576+ id: notification
577+
578+ height: 0.7 * backgroundCircle.width
579+ width: notification.height
580+ anchors.centerIn: parent
581+
582+ text: infographic.model.label
583+ font.pixelSize: notification.height / 10
584+
585+ wrapMode: Text.WordWrap
586+ horizontalAlignment: Text.AlignHCenter
587+ verticalAlignment: Text.AlignVCenter
588+ color: "white"
589+
590+ opacity: 0.0
591+
592+ property variant showAnim: increaseOpacity
593+
594+ PropertyAnimation {
595+ id: increaseOpacity
596+
597+ target: notification
598+ property: "opacity"
599+ to: 1.0
600+ duration: dotAnimTimer.interval * dots.count * 2
601+ }
602+ }
603+ }
604+
605+ MouseArea {
606+ anchors.fill: dataCircle
607+
608+ onClicked: {
609+ infographic.model.nextDataSource();
610+ }
611+ }
612+}
613
614=== added file 'Greeter/Infographics.qmlproject'
615--- Greeter/Infographics.qmlproject 1970-01-01 00:00:00 +0000
616+++ Greeter/Infographics.qmlproject 2013-05-10 12:09:35 +0000
617@@ -0,0 +1,20 @@
618+/* File generated by Qt Creator, version 2.7.0 */
619+
620+import QmlProject 1.1
621+
622+Project {
623+ mainFile: "Infographics.qml"
624+
625+ /* Include .qml, .js, and image files from current directory and subdirectories */
626+ QmlFiles {
627+ directory: "."
628+ }
629+ JavaScriptFiles {
630+ directory: "."
631+ }
632+ ImageFiles {
633+ directory: "."
634+ }
635+ /* List of plugin directories passed to QML runtime */
636+ // importPaths: [ "../exampleplugin" ]
637+}
638
639=== modified file 'Greeter/LoginList.qml'
640--- Greeter/LoginList.qml 2013-04-12 05:54:57 +0000
641+++ Greeter/LoginList.qml 2013-05-10 12:09:35 +0000
642@@ -16,17 +16,18 @@
643
644 import QtQuick 2.0
645 import Ubuntu.Components 0.1
646+import LightDM 0.1 as LightDM
647
648 Item {
649 id: root
650
651+ property alias userList: userList
652 property alias model: userList.model
653 property alias currentIndex: userList.currentIndex
654
655 readonly property int cellHeight: units.gu(6)
656 readonly property int highlightedHeight: units.gu(10)
657 readonly property int moveDuration: 200
658- readonly property bool guestAccount: userList.model.get(userList.currentIndex).password === undefined
659
660 signal selected(int uid)
661 signal unlocked(int uid)
662@@ -66,9 +67,18 @@
663 readonly property int offsetStartDistance: units.gu(7)
664 readonly property int fadeOutDistance: pathStartMargin - units.gu(1)
665
666+ onCountChanged: {
667+ // Start authentication when we start up
668+ if (!LightDM.Greeter.authenticationUser && userList.currentItem) {
669+ root.selected(userList.currentIndex);
670+ root.resetAuthentication();
671+ }
672+ }
673+
674 onCurrentIndexChanged: {
675- passwordInput.text = "";
676- passwordInput.focus = false
677+ if (LightDM.Greeter.authenticationUser != userList.model.data(currentIndex, LightDM.UserRoles.NameRole)) {
678+ root.resetAuthentication();
679+ }
680 }
681
682 onMovingInternallyChanged: {
683@@ -157,7 +167,7 @@
684 right: parent.right
685 rightMargin: units.gu(2)
686 }
687- text: name
688+ text: realName
689 color: "white"
690 elide: Text.ElideRight
691 }
692@@ -201,7 +211,7 @@
693 }
694 height: units.gu(4.5)
695 width: parent.width - anchors.margins * 2
696- placeholderText: root.guestAccount ? "Tap to unlock" : "Password"
697+ placeholderText: LightDM.Greeter.promptless ? "Tap to unlock" : "Password"
698 echoMode: TextInput.Password
699 opacity: userList.movingInternally ? 0 : 1
700
701@@ -209,15 +219,7 @@
702 NumberAnimation { duration: 100 }
703 }
704
705- onAccepted: {
706- if (text === userList.model.get(userList.currentIndex).password) {
707- root.unlocked(userList.currentIndex);
708- passwordInput.focus = false;
709- } else {
710- wrongPasswordAnimation.start();
711- }
712- text = "";
713- }
714+ onAccepted: LightDM.Greeter.respond(text)
715
716 Image {
717 anchors {
718@@ -225,7 +227,7 @@
719 rightMargin: units.gu(2)
720 verticalCenter: parent.verticalCenter
721 }
722- visible: root.guestAccount
723+ visible: LightDM.Greeter.promptless
724 source: "graphics/icon_arrow.png"
725 }
726
727@@ -265,11 +267,38 @@
728 }
729
730 }
731+
732 MouseArea {
733 anchors.fill: passwordInput
734- enabled: root.guestAccount
735+ enabled: LightDM.Greeter.promptless
736 onClicked: {
737 root.unlocked(userList.currentIndex);
738 }
739 }
740+
741+ function resetAuthentication() {
742+ if (!userList.currentItem) {
743+ return;
744+ }
745+ passwordInput.text = "";
746+ passwordInput.focus = false;
747+ LightDM.Greeter.authenticate(userList.model.data(currentIndex, LightDM.UserRoles.NameRole));
748+ }
749+
750+ Connections {
751+ target: LightDM.Greeter
752+
753+ onAuthenticationComplete: {
754+ if (LightDM.Greeter.promptless) {
755+ return;
756+ }
757+ if (LightDM.Greeter.authenticated) {
758+ root.unlocked(userList.currentIndex);
759+ passwordInput.focus = false;
760+ } else {
761+ wrongPasswordAnimation.start();
762+ }
763+ passwordInput.text = "";
764+ }
765+ }
766 }
767
768=== added file 'Greeter/graphics/dot_empty@30.png'
769Binary files Greeter/graphics/dot_empty@30.png 1970-01-01 00:00:00 +0000 and Greeter/graphics/dot_empty@30.png 2013-05-10 12:09:35 +0000 differ
770=== added file 'Greeter/graphics/dot_filled@30.png'
771Binary files Greeter/graphics/dot_filled@30.png 1970-01-01 00:00:00 +0000 and Greeter/graphics/dot_filled@30.png 2013-05-10 12:09:35 +0000 differ
772=== added file 'Greeter/graphics/dot_pointer@30.png'
773Binary files Greeter/graphics/dot_pointer@30.png 1970-01-01 00:00:00 +0000 and Greeter/graphics/dot_pointer@30.png 2013-05-10 12:09:35 +0000 differ
774=== added file 'Greeter/graphics/infographic_circle.png'
775Binary files Greeter/graphics/infographic_circle.png 1970-01-01 00:00:00 +0000 and Greeter/graphics/infographic_circle.png 2013-05-10 12:09:35 +0000 differ
776=== removed file 'Greeter/graphics/infographic_default@20.png'
777Binary files Greeter/graphics/infographic_default@20.png 2013-02-12 17:43:28 +0000 and Greeter/graphics/infographic_default@20.png 1970-01-01 00:00:00 +0000 differ
778=== removed file 'Greeter/graphics/infographic_tweeter_cut@18.png'
779Binary files Greeter/graphics/infographic_tweeter_cut@18.png 2012-11-23 17:15:36 +0000 and Greeter/graphics/infographic_tweeter_cut@18.png 1970-01-01 00:00:00 +0000 differ
780=== modified file 'Shell.qml'
781--- Shell.qml 2013-05-02 22:07:11 +0000
782+++ Shell.qml 2013-05-10 12:09:35 +0000
783@@ -17,6 +17,7 @@
784 import QtQuick 2.0
785 import Ubuntu.Application 0.1
786 import Ubuntu.Components 0.1
787+import LightDM 0.1 as LightDM
788 import "Dash"
789 import "Greeter"
790 import "Launcher"
791@@ -36,7 +37,8 @@
792 height: tablet ? units.gu(100) : units.gu(71)
793
794 property real edgeSize: units.gu(2)
795- property url background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
796+ property url default_background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
797+ property url background: default_background
798 readonly property real panelHeight: panel.panelHeight
799
800 property bool dashShown: dash.shown
801@@ -351,8 +353,10 @@
802 onShownChanged: if (shown) greeter.forceActiveFocus()
803
804 onUnlocked: greeter.hide()
805- onSelected: shell.background = greeter.model.get(uid).background;
806-
807+ onSelected: {
808+ var bgPath = greeter.model.data(uid, LightDM.UserRoles.BackgroundPathRole)
809+ shell.background = bgPath ? bgPath : default_background
810+ }
811 }
812
813 Revealer {
814
815=== modified file 'debian/control'
816--- debian/control 2013-05-09 08:55:23 +0000
817+++ debian/control 2013-05-10 12:09:35 +0000
818@@ -4,7 +4,6 @@
819 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
820 Build-Depends: cmake,
821 debhelper (>= 9),
822- demo-assets,
823 doxygen,
824 graphviz,
825 libboost-regex1.49-dev (>= 1.49),
826
827=== modified file 'debian/qml-phone-shell.install'
828--- debian/qml-phone-shell.install 2013-04-18 18:53:42 +0000
829+++ debian/qml-phone-shell.install 2013-05-10 12:09:35 +0000
830@@ -12,5 +12,6 @@
831 /usr/share/qml-phone-shell/SideStage/*
832 /usr/share/qml-phone-shell/graphics/*
833 /usr/share/qml-phone-shell/plugins/HudClient/*
834+/usr/share/qml-phone-shell/plugins/LightDM/*
835 /usr/share/qml-phone-shell/plugins/Unity/*
836 /usr/share/qml-phone-shell/plugins/Utils/*
837
838=== modified file 'plugins/CMakeLists.txt'
839--- plugins/CMakeLists.txt 2013-04-18 17:20:37 +0000
840+++ plugins/CMakeLists.txt 2013-05-10 12:09:35 +0000
841@@ -1,3 +1,4 @@
842 add_subdirectory(Utils)
843 add_subdirectory(Unity)
844 add_subdirectory(HudClient)
845+add_subdirectory(LightDM)
846
847=== added directory 'plugins/LightDM'
848=== added file 'plugins/LightDM/CMakeLists.txt'
849--- plugins/LightDM/CMakeLists.txt 1970-01-01 00:00:00 +0000
850+++ plugins/LightDM/CMakeLists.txt 2013-05-10 12:09:35 +0000
851@@ -0,0 +1,49 @@
852+# Dependencies
853+# TODO: Once we split out a separate greeter process, uncomment these lines
854+#include(FindPkgConfig)
855+#pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-2)
856+
857+include_directories(
858+ ${CMAKE_CURRENT_SOURCE_DIR}
859+ ${CMAKE_CURRENT_BINARY_DIR}
860+ ${LIBLIGHTDM_INCLUDE_DIRS}
861+)
862+
863+set(QMLPLUGIN_SRC
864+ ../Utils/qsortfilterproxymodelqml.cpp # FIXME evaluate a more generic approach for using other plugins
865+ greeter.cpp
866+ infographicmodel.cpp
867+ plugin.cpp
868+ usersmodel.cpp
869+ # TODO: Once we split out a separate greeter process, we should move these
870+ # files to a mock plugin and link this plugin to the real liblightdm
871+ lightdm-greeter.cpp
872+ lightdm-usersmodel.cpp
873+ lightdm-infographicmodel.cpp
874+ qvariantlistmodel.cpp
875+ )
876+
877+add_library(LightDM-qml MODULE
878+ ${QMLPLUGIN_SRC}
879+ )
880+
881+# TODO: Once we split out a separate greeter process, uncomment these lines
882+#target_link_libraries(LightDM-qml
883+# ${LIBLIGHTDM_LDFLAGS}
884+# )
885+
886+qt5_use_modules(LightDM-qml Gui Qml)
887+
888+# copy qmldir file into build directory for shadow builds
889+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
890+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
891+ )
892+
893+install(TARGETS LightDM-qml
894+ DESTINATION ${SHELL_APP_DIR}/plugins/LightDM
895+ )
896+
897+install(FILES qmldir
898+ DESTINATION ${SHELL_APP_DIR}/plugins/LightDM
899+ )
900+
901
902=== added file 'plugins/LightDM/greeter.cpp'
903--- plugins/LightDM/greeter.cpp 1970-01-01 00:00:00 +0000
904+++ plugins/LightDM/greeter.cpp 2013-05-10 12:09:35 +0000
905@@ -0,0 +1,133 @@
906+/*
907+ * Copyright (C) 2013 Canonical, Ltd.
908+ *
909+ * This program is free software; you can redistribute it and/or modify
910+ * it under the terms of the GNU General Public License as published by
911+ * the Free Software Foundation; version 3.
912+ *
913+ * This program is distributed in the hope that it will be useful,
914+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
915+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
916+ * GNU General Public License for more details.
917+ *
918+ * You should have received a copy of the GNU General Public License
919+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
920+ *
921+ * Author: Michael Terry <michael.terry@canonical.com>
922+ */
923+
924+#include "greeter.h"
925+#include "lightdm-greeter.h"
926+
927+class GreeterPrivate
928+{
929+public:
930+ explicit GreeterPrivate(Greeter *parent);
931+
932+ QLightDM::Greeter *m_greeter;
933+ bool wasPrompted;
934+ bool promptless;
935+
936+protected:
937+ Greeter * const q_ptr;
938+
939+private:
940+ Q_DECLARE_PUBLIC(Greeter)
941+};
942+
943+
944+GreeterPrivate::GreeterPrivate(Greeter* parent)
945+ : m_greeter(new QLightDM::Greeter(parent)),
946+ wasPrompted(false),
947+ promptless(false),
948+ q_ptr(parent)
949+{
950+}
951+
952+Greeter::Greeter(QObject* parent)
953+ : QObject(parent),
954+ d_ptr(new GreeterPrivate(this))
955+{
956+ Q_D(Greeter);
957+
958+ connect(d->m_greeter, SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)),
959+ this, SLOT(showMessageFilter(QString, QLightDM::Greeter::MessageType)));
960+ connect(d->m_greeter, SIGNAL(showPrompt(QString, QLightDM::Greeter::PromptType)),
961+ this, SLOT(showPromptFilter(QString, QLightDM::Greeter::PromptType)));
962+ connect(d->m_greeter, SIGNAL(authenticationComplete()),
963+ this, SLOT(authenticationCompleteFilter()));
964+
965+ d->m_greeter->connectSync();
966+}
967+
968+bool Greeter::isAuthenticated() const
969+{
970+ Q_D(const Greeter);
971+ return d->m_greeter->isAuthenticated();
972+}
973+
974+QString Greeter::authenticationUser() const
975+{
976+ Q_D(const Greeter);
977+ return d->m_greeter->authenticationUser();
978+}
979+
980+bool Greeter::promptless() const
981+{
982+ Q_D(const Greeter);
983+ return d->promptless;
984+}
985+
986+void Greeter::authenticate(const QString &username)
987+{
988+ Q_D(Greeter);
989+ d->wasPrompted = false;
990+ if (d->promptless) {
991+ d->promptless = false;
992+ Q_EMIT promptlessChanged();
993+ }
994+
995+ d->m_greeter->authenticate(username);
996+}
997+
998+void Greeter::respond(const QString &response)
999+{
1000+ Q_D(Greeter);
1001+ d->m_greeter->respond(response);
1002+}
1003+
1004+bool Greeter::startSessionSync(const QString &session)
1005+{
1006+ Q_D(Greeter);
1007+ return d->m_greeter->startSessionSync(session);
1008+}
1009+
1010+void Greeter::showPromptFilter(QString text, QLightDM::Greeter::PromptType type)
1011+{
1012+ Q_D(Greeter);
1013+ d->wasPrompted = true;
1014+
1015+ // Strip prompt of any colons at the end
1016+ text = text.trimmed();
1017+ if (text.endsWith(":") || text.endsWith("")) {
1018+ text.chop(1);
1019+ }
1020+
1021+ Q_EMIT showPrompt(text, type == QLightDM::Greeter::PromptTypeSecret);
1022+}
1023+
1024+void Greeter::showMessageFilter(QString text, QLightDM::Greeter::MessageType type)
1025+{
1026+ Q_EMIT showMessage(text, type == QLightDM::Greeter::MessageTypeError);
1027+}
1028+
1029+void Greeter::authenticationCompleteFilter()
1030+{
1031+ Q_D(Greeter);
1032+ if (!d->wasPrompted) {
1033+ d->promptless = true;
1034+ Q_EMIT promptlessChanged();
1035+ }
1036+
1037+ Q_EMIT authenticationComplete();
1038+}
1039
1040=== added file 'plugins/LightDM/greeter.h'
1041--- plugins/LightDM/greeter.h 1970-01-01 00:00:00 +0000
1042+++ plugins/LightDM/greeter.h 2013-05-10 12:09:35 +0000
1043@@ -0,0 +1,70 @@
1044+/*
1045+ * Copyright (C) 2012,2013 Canonical, Ltd.
1046+ *
1047+ * This program is free software; you can redistribute it and/or modify
1048+ * it under the terms of the GNU General Public License as published by
1049+ * the Free Software Foundation; version 3.
1050+ *
1051+ * This program is distributed in the hope that it will be useful,
1052+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1053+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1054+ * GNU General Public License for more details.
1055+ *
1056+ * You should have received a copy of the GNU General Public License
1057+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1058+ *
1059+ * Authors: Michael Terry <michael.terry@canonical.com>
1060+ */
1061+
1062+/* This class is a really tiny filter around QLightDM::Greeter. There are some
1063+ operations that we want to edit a bit for the benefit of Qml. Specifically,
1064+ we want to chop colons off of any password prompts. But there may be more
1065+ such edits in the future, and by inserting ourselves here, we have more
1066+ control. */
1067+
1068+#ifndef GREETER_H
1069+#define GREETER_H
1070+
1071+#include "lightdm-greeter.h"
1072+#include <QtCore/QObject>
1073+
1074+class GreeterPrivate;
1075+
1076+class Greeter : public QObject
1077+{
1078+ Q_OBJECT
1079+
1080+ Q_PROPERTY(bool authenticated READ isAuthenticated)
1081+ Q_PROPERTY(QString authenticationUser READ authenticationUser)
1082+ Q_PROPERTY(bool promptless READ promptless NOTIFY promptlessChanged)
1083+
1084+public:
1085+ explicit Greeter(QObject* parent=0);
1086+
1087+ bool isAuthenticated() const;
1088+ QString authenticationUser() const;
1089+ bool promptless() const;
1090+
1091+public Q_SLOTS:
1092+ void authenticate(const QString &username=QString());
1093+ void respond(const QString &response);
1094+ bool startSessionSync(const QString &session=QString());
1095+
1096+Q_SIGNALS:
1097+ void showMessage(QString text, bool isError);
1098+ void showPrompt(QString text, bool isSecret);
1099+ void authenticationComplete();
1100+ void promptlessChanged();
1101+
1102+private:
1103+ GreeterPrivate * const d_ptr;
1104+
1105+ Q_DECLARE_PRIVATE(Greeter)
1106+
1107+private Q_SLOTS:
1108+ void showMessageFilter(QString text, QLightDM::Greeter::MessageType type);
1109+ void showPromptFilter(QString text, QLightDM::Greeter::PromptType type);
1110+ void authenticationCompleteFilter();
1111+};
1112+
1113+#endif
1114
1115=== added file 'plugins/LightDM/infographicmodel.cpp'
1116--- plugins/LightDM/infographicmodel.cpp 1970-01-01 00:00:00 +0000
1117+++ plugins/LightDM/infographicmodel.cpp 2013-05-10 12:09:35 +0000
1118@@ -0,0 +1,21 @@
1119+/*
1120+ * Copyright (C) 2012,2013 Canonical, Ltd.
1121+ *
1122+ * This program is free software; you can redistribute it and/or modify
1123+ * it under the terms of the GNU General Public License as published by
1124+ * the Free Software Foundation; version 3.
1125+ *
1126+ * This program is distributed in the hope that it will be useful,
1127+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1128+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1129+ * GNU General Public License for more details.
1130+ *
1131+ * You should have received a copy of the GNU General Public License
1132+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1133+ *
1134+ * Authors: Pete Woods <pete.woods@canonical.com>
1135+ */
1136+
1137+#include "infographicmodel.h"
1138+
1139+#include "infographicmodel.moc"
1140
1141=== added file 'plugins/LightDM/infographicmodel.h'
1142--- plugins/LightDM/infographicmodel.h 1970-01-01 00:00:00 +0000
1143+++ plugins/LightDM/infographicmodel.h 2013-05-10 12:09:35 +0000
1144@@ -0,0 +1,26 @@
1145+/*
1146+ * Copyright (C) 2012,2013 Canonical, Ltd.
1147+ *
1148+ * This program is free software; you can redistribute it and/or modify
1149+ * it under the terms of the GNU General Public License as published by
1150+ * the Free Software Foundation; version 3.
1151+ *
1152+ * This program is distributed in the hope that it will be useful,
1153+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1154+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1155+ * GNU General Public License for more details.
1156+ *
1157+ * You should have received a copy of the GNU General Public License
1158+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1159+ *
1160+ * Authors: Pete Woods <pete.woods@canonical.com>
1161+ */
1162+
1163+#ifndef INFOGRAPHICMODEL_H
1164+#define INFOGRAPHICMODEL_H
1165+
1166+#include "lightdm-infographicmodel.h"
1167+
1168+typedef QLightDM::InfographicModel InfographicModel;
1169+
1170+#endif
1171
1172=== added file 'plugins/LightDM/lightdm-greeter.cpp'
1173--- plugins/LightDM/lightdm-greeter.cpp 1970-01-01 00:00:00 +0000
1174+++ plugins/LightDM/lightdm-greeter.cpp 2013-05-10 12:09:35 +0000
1175@@ -0,0 +1,195 @@
1176+/*
1177+ * Copyright (C) 2013 Canonical, Ltd.
1178+ *
1179+ * This program is free software; you can redistribute it and/or modify
1180+ * it under the terms of the GNU General Public License as published by
1181+ * the Free Software Foundation; version 3.
1182+ *
1183+ * This program is distributed in the hope that it will be useful,
1184+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1185+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1186+ * GNU General Public License for more details.
1187+ *
1188+ * You should have received a copy of the GNU General Public License
1189+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1190+ *
1191+ * Author: Michael Terry <michael.terry@canonical.com>
1192+ */
1193+
1194+#include "lightdm-greeter.h"
1195+#include <QtCore/QCoreApplication>
1196+
1197+namespace QLightDM
1198+{
1199+
1200+class GreeterPrivate
1201+{
1202+public:
1203+ explicit GreeterPrivate(Greeter *parent);
1204+
1205+ bool authenticated;
1206+ QString authenticationUser;
1207+
1208+protected:
1209+ Greeter * const q_ptr;
1210+
1211+private:
1212+ Q_DECLARE_PUBLIC(Greeter)
1213+};
1214+
1215+GreeterPrivate::GreeterPrivate(Greeter* parent)
1216+ : authenticated(false),
1217+ authenticationUser(),
1218+ q_ptr(parent)
1219+{
1220+}
1221+
1222+Greeter::Greeter(QObject *parent)
1223+ : QObject(parent),
1224+ d_ptr(new GreeterPrivate(this))
1225+{
1226+}
1227+
1228+Greeter::~Greeter()
1229+{
1230+}
1231+
1232+QString Greeter::authenticationUser() const
1233+{
1234+ Q_D(const Greeter);
1235+ return d->authenticationUser;
1236+}
1237+
1238+bool Greeter::hasGuestAccountHint() const
1239+{
1240+ return true;
1241+}
1242+
1243+QString Greeter::getHint(const QString &name) const
1244+{
1245+ Q_UNUSED(name)
1246+ return "";
1247+}
1248+
1249+QString Greeter::defaultSessionHint() const
1250+{
1251+ return "ubuntu";
1252+}
1253+
1254+bool Greeter::hideUsersHint() const
1255+{
1256+ return false;
1257+}
1258+
1259+bool Greeter::showManualLoginHint() const
1260+{
1261+ return true;
1262+}
1263+
1264+bool Greeter::showRemoteLoginHint() const
1265+{
1266+ return true;
1267+}
1268+
1269+bool Greeter::lockHint () const
1270+{
1271+ return false;
1272+}
1273+
1274+QString Greeter::selectUserHint() const
1275+{
1276+ return "";
1277+}
1278+
1279+bool Greeter::selectGuestHint() const
1280+{
1281+ return false;
1282+}
1283+
1284+QString Greeter::autologinUserHint() const
1285+{
1286+ return "";
1287+}
1288+
1289+bool Greeter::autologinGuestHint() const
1290+{
1291+ return false;
1292+}
1293+
1294+int Greeter::autologinTimeoutHint() const
1295+{
1296+ return 0;
1297+}
1298+
1299+bool Greeter::inAuthentication() const
1300+{
1301+ return false;
1302+}
1303+
1304+QString Greeter::hostname() const
1305+{
1306+ return "hostname1";
1307+}
1308+
1309+bool Greeter::isAuthenticated() const
1310+{
1311+ Q_D(const Greeter);
1312+ return d->authenticated;
1313+}
1314+
1315+bool Greeter::connectSync()
1316+{
1317+ return true;
1318+}
1319+
1320+void Greeter::authenticate(const QString &username)
1321+{
1322+ Q_D(Greeter);
1323+
1324+ d->authenticated = false;
1325+ d->authenticationUser = username;
1326+
1327+ if (d->authenticationUser == "no-password") {
1328+ d->authenticated = true;
1329+ Q_EMIT authenticationComplete();
1330+ } else {
1331+ Q_EMIT showPrompt("Password: ", PromptTypeSecret);
1332+ }
1333+}
1334+
1335+void Greeter::authenticateAsGuest()
1336+{}
1337+
1338+void Greeter::authenticateAutologin()
1339+{}
1340+
1341+void Greeter::authenticateRemote(const QString &session, const QString &username)
1342+{
1343+ Q_UNUSED(session)
1344+ Q_UNUSED(username)
1345+}
1346+
1347+void Greeter::cancelAuthentication()
1348+{}
1349+
1350+void Greeter::setLanguage (const QString &language)
1351+{
1352+ Q_UNUSED(language)
1353+}
1354+
1355+bool Greeter::startSessionSync(const QString &session)
1356+{
1357+ Q_UNUSED(session)
1358+ QCoreApplication::quit();
1359+ return true;
1360+}
1361+
1362+void Greeter::respond(const QString &response)
1363+{
1364+ Q_D(Greeter);
1365+
1366+ d->authenticated = (response == "password");
1367+ Q_EMIT authenticationComplete();
1368+}
1369+
1370+}
1371
1372=== added file 'plugins/LightDM/lightdm-greeter.h'
1373--- plugins/LightDM/lightdm-greeter.h 1970-01-01 00:00:00 +0000
1374+++ plugins/LightDM/lightdm-greeter.h 2013-05-10 12:09:35 +0000
1375@@ -0,0 +1,105 @@
1376+/*
1377+ * Copyright (C) 2013 Canonical, Ltd.
1378+ * Copyright (C) 2010-2011 David Edmundson.
1379+ * Copyright (C) 2010-2011 Robert Ancell
1380+ *
1381+ * This program is free software; you can redistribute it and/or modify
1382+ * it under the terms of the GNU General Public License as published by
1383+ * the Free Software Foundation; version 3.
1384+ *
1385+ * This program is distributed in the hope that it will be useful,
1386+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1387+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1388+ * GNU General Public License for more details.
1389+ *
1390+ * You should have received a copy of the GNU General Public License
1391+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1392+ *
1393+ * Author: David Edmundson <kde@davidedmundson.co.uk>
1394+ */
1395+
1396+#ifndef LIGHTDM_GREETER_H
1397+#define LIGHTDM_GREETER_H
1398+
1399+#include <QtCore/QObject>
1400+#include <QtCore/QVariant>
1401+
1402+
1403+namespace QLightDM
1404+{
1405+ class GreeterPrivate;
1406+
1407+class Q_DECL_EXPORT Greeter : public QObject
1408+{
1409+ Q_OBJECT
1410+
1411+ Q_PROPERTY(bool authenticated READ isAuthenticated ) //NOTFIY authenticationComplete
1412+ Q_PROPERTY(QString authenticationUser READ authenticationUser )
1413+ Q_PROPERTY(QString defaultSession READ defaultSessionHint CONSTANT)
1414+ Q_PROPERTY(QString selectUser READ selectUserHint CONSTANT)
1415+ Q_PROPERTY(bool selectGuest READ selectGuestHint CONSTANT)
1416+
1417+ Q_PROPERTY(QString hostname READ hostname CONSTANT)
1418+ Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
1419+ Q_PROPERTY(bool locked READ lockHint CONSTANT)
1420+
1421+ Q_PROPERTY(QString hostname READ hostname CONSTANT)
1422+
1423+ Q_ENUMS(PromptType MessageType)
1424+
1425+public:
1426+ enum PromptType {
1427+ PromptTypeQuestion,
1428+ PromptTypeSecret
1429+ };
1430+
1431+ enum MessageType {
1432+ MessageTypeInfo,
1433+ MessageTypeError
1434+ };
1435+
1436+ explicit Greeter(QObject* parent=0);
1437+ virtual ~Greeter();
1438+
1439+ QString getHint(const QString &name) const;
1440+ QString defaultSessionHint() const;
1441+ bool hideUsersHint() const;
1442+ bool showManualLoginHint() const;
1443+ bool showRemoteLoginHint() const;
1444+ bool lockHint () const;
1445+ bool hasGuestAccountHint() const;
1446+ QString selectUserHint() const;
1447+ bool selectGuestHint() const;
1448+ QString autologinUserHint() const;
1449+ bool autologinGuestHint() const;
1450+ int autologinTimeoutHint() const;
1451+
1452+ bool inAuthentication() const;
1453+ bool isAuthenticated() const;
1454+ QString authenticationUser() const;
1455+ QString hostname() const;
1456+
1457+public Q_SLOTS:
1458+ bool connectSync();
1459+ void authenticate(const QString &username=QString());
1460+ void authenticateAsGuest();
1461+ void authenticateAutologin();
1462+ void authenticateRemote(const QString &session=QString(), const QString &username=QString());
1463+ void respond(const QString &response);
1464+ void cancelAuthentication();
1465+ void setLanguage (const QString &language);
1466+ bool startSessionSync(const QString &session=QString());
1467+
1468+Q_SIGNALS:
1469+ void showMessage(QString text, QLightDM::Greeter::MessageType type);
1470+ void showPrompt(QString text, QLightDM::Greeter::PromptType type);
1471+ void authenticationComplete();
1472+ void autologinTimerExpired();
1473+
1474+private:
1475+ GreeterPrivate *d_ptr;
1476+ Q_DECLARE_PRIVATE(Greeter)
1477+};
1478+}
1479+
1480+#endif // LIGHTDM_GREETER_H
1481
1482=== added file 'plugins/LightDM/lightdm-infographicmodel.cpp'
1483--- plugins/LightDM/lightdm-infographicmodel.cpp 1970-01-01 00:00:00 +0000
1484+++ plugins/LightDM/lightdm-infographicmodel.cpp 2013-05-10 12:09:35 +0000
1485@@ -0,0 +1,370 @@
1486+/*
1487+ * Copyright (C) 2013 Canonical, Ltd.
1488+ *
1489+ * This program is free software; you can redistribute it and/or modify
1490+ * it under the terms of the GNU General Public License as published by
1491+ * the Free Software Foundation; version 3.
1492+ *
1493+ * This program is distributed in the hope that it will be useful,
1494+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1495+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1496+ * GNU General Public License for more details.
1497+ *
1498+ * You should have received a copy of the GNU General Public License
1499+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1500+ *
1501+ * Author: Pete Woods <pete.woods@canonical.com>
1502+ */
1503+
1504+// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
1505+// To use the same method of setting role name that it does, we
1506+// set our compatibility to Qt4 here too.
1507+#define QT_DISABLE_DEPRECATED_BEFORE QT_VERSION_CHECK(4, 0, 0)
1508+
1509+#include "lightdm-infographicmodel.h"
1510+#include <QtCore/QDebug>
1511+#include <QtCore/QDir>
1512+#include <QtCore/QString>
1513+#include <QtGui/QIcon>
1514+
1515+#include <QMultiMap>
1516+
1517+#include <random>
1518+#include <iostream>
1519+
1520+using namespace QLightDM;
1521+
1522+namespace QLightDM
1523+{
1524+
1525+class InfographicData
1526+{
1527+public:
1528+ explicit InfographicData(QObject *parent);
1529+
1530+ explicit InfographicData(QString label, QColor first_color,
1531+ QVariantList &first_month, QColor second_color,
1532+ QVariantList &second_month, QObject *parent);
1533+
1534+public:
1535+ QObject * const q_ptr;
1536+ QString label;
1537+ QColor first_color;
1538+ QVariantListModel first_month;
1539+ QColor second_color;
1540+ QVariantListModel second_month;
1541+
1542+private:
1543+ Q_DECLARE_PUBLIC(QObject)
1544+};
1545+
1546+class InfographicModelPrivate: QObject
1547+{
1548+Q_OBJECT
1549+
1550+public:
1551+ explicit InfographicModelPrivate(InfographicModel *parent);
1552+
1553+ void generateFakeData();
1554+
1555+ void nextFakeData();
1556+
1557+protected:
1558+ InfographicModel * const q_ptr;
1559+ InfographicData data;
1560+ int current_day;
1561+ QString username;
1562+ int data_index;
1563+ QMultiMap<QString, QSharedPointer<InfographicData>> fake_data;
1564+ QSharedPointer<InfographicData> empty_data;
1565+
1566+protected:
1567+ void setFakeData(int data_index);
1568+
1569+private:
1570+ Q_DECLARE_PUBLIC(InfographicModel)
1571+};
1572+}
1573+
1574+InfographicData::InfographicData(QObject *parent) :
1575+ q_ptr(parent)
1576+{
1577+}
1578+
1579+InfographicData::InfographicData(QString label, QColor first_color,
1580+ QVariantList &first_month, QColor second_color,
1581+ QVariantList &second_month, QObject* parent) :
1582+ q_ptr(parent), label(label), first_color(first_color), first_month(
1583+ first_month, parent), second_color(second_color), second_month(
1584+ second_month, parent)
1585+{
1586+}
1587+
1588+InfographicModelPrivate::InfographicModelPrivate(InfographicModel *parent) :
1589+ q_ptr(parent), data(this), current_day(0)
1590+{
1591+ generateFakeData();
1592+ setFakeData(0);
1593+}
1594+
1595+void InfographicModelPrivate::generateFakeData()
1596+{
1597+ std::default_random_engine generator;
1598+ std::normal_distribution<qreal> distribution(0.5, 0.2);
1599+ auto rand = std::bind(distribution, generator);
1600+
1601+ QColor orange = QColor::fromRgbF(0.9, 0.3, 0.1, 1.0);
1602+ QColor yellow = QColor::fromRgbF(1.0, 0.6, 0.0, 1.0);
1603+ QColor red = QColor::fromRgbF(0.8, 0.0, 0.0, 1.0);
1604+ QColor dark_purple = QColor::fromRgbF(0.5, 0.2, 0.3, 1.0);
1605+ QColor light_purple = QColor::fromRgbF(0.8, 0.1, 0.8, 1.0);
1606+
1607+ {
1608+ QVariantList month;
1609+ QColor black;
1610+ empty_data.reset(
1611+ new InfographicData("", black, month, black, month, this));
1612+ }
1613+
1614+ {
1615+ QVariantList first_month( { 0.1, 0.2, 0.4, 0.6, 0.1, 0.2 });
1616+ while (first_month.size() < 31)
1617+ first_month.push_back(QVariant());
1618+ QVariantList second_month;
1619+ while (second_month.size() < 31)
1620+ second_month.push_back(QVariant(rand()));
1621+ QSharedPointer<InfographicData> data(
1622+ new InfographicData("Wow, you've walked <b>5</b>kms today",
1623+ yellow, first_month, orange, second_month, this));
1624+ fake_data.insert("phablet", data);
1625+ }
1626+
1627+ {
1628+ QVariantList first_month(
1629+ { 0.1, 0.2, 1.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 0.8, 0.2, 0.3,
1630+ 0.4, 0.2, 0.3, 0.4, 0.5, 0.7, 0.8, 0.5, 0.7, 0.8 });
1631+ while (first_month.size() < 31)
1632+ first_month.push_back(QVariant());
1633+ QVariantList second_month;
1634+ while (second_month.size() < 31)
1635+ second_month.push_back(QVariant(rand()));
1636+ QSharedPointer<InfographicData> data(
1637+ new InfographicData("You've received <b>15</b> messages today",
1638+ orange, first_month, red, second_month, this));
1639+ fake_data.insert("phablet", data);
1640+ }
1641+
1642+ {
1643+ QVariantList first_month( { 0.1, 0.2, 1.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.7,
1644+ 0.8, 0.9, 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1 });
1645+ while (first_month.size() < 31)
1646+ first_month.push_back(QVariant());
1647+ QVariantList second_month;
1648+ while (second_month.size() < 31)
1649+ second_month.push_back(QVariant(rand()));
1650+ QSharedPointer<InfographicData> data(
1651+ new InfographicData("Klout score of <b>50.20</b>", light_purple,
1652+ first_month, dark_purple, second_month, this));
1653+ fake_data.insert("phablet", data);
1654+ }
1655+
1656+ {
1657+ QVariantList first_month( { 0.1, 0.2, 1.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.7,
1658+ 0.8, 1.0, 0.2, 0.6, 0.1, 0.8 });
1659+ while (first_month.size() < 31)
1660+ first_month.push_back(QVariant(rand()));
1661+ QVariantList second_month;
1662+ while (second_month.size() < 31)
1663+ second_month.push_back(QVariant(rand()));
1664+ QSharedPointer<InfographicData> data(
1665+ new InfographicData("<b>1</b>hr watching videos today", red,
1666+ first_month, dark_purple, second_month, this));
1667+ fake_data.insert("phablet", data);
1668+ }
1669+
1670+ {
1671+ QVariantList first_month;
1672+ while (first_month.size() < 5)
1673+ first_month.push_back(QVariant(rand()));
1674+ while (first_month.size() < 31)
1675+ first_month.push_back(QVariant());
1676+ QVariantList second_month;
1677+ while (second_month.size() < 31)
1678+ second_month.push_back(QVariant(rand()));
1679+ QSharedPointer<InfographicData> data(
1680+ new InfographicData("<b>4</b>hrs watching videos today", red,
1681+ first_month, dark_purple, second_month, this));
1682+ fake_data.insert("anna", data);
1683+ }
1684+
1685+ {
1686+ QVariantList first_month;
1687+ while (first_month.size() < 20)
1688+ first_month.push_back(QVariant(rand()));
1689+ while (first_month.size() < 31)
1690+ first_month.push_back(QVariant());
1691+ QVariantList second_month;
1692+ while (second_month.size() < 31)
1693+ second_month.push_back(QVariant(rand()));
1694+ QSharedPointer<InfographicData> data(
1695+ new InfographicData("You've received <b>23</b> messages today",
1696+ orange, first_month, red, second_month, this));
1697+ fake_data.insert("anna", data);
1698+ }
1699+
1700+ {
1701+ QVariantList first_month;
1702+ while (first_month.size() < 2)
1703+ first_month.push_back(QVariant(rand()));
1704+ while (first_month.size() < 31)
1705+ first_month.push_back(QVariant());
1706+ QVariantList second_month;
1707+ while (second_month.size() < 31)
1708+ second_month.push_back(QVariant(rand()));
1709+ QSharedPointer<InfographicData> data(
1710+ new InfographicData("<b>8</b>hrs watching videos today", red,
1711+ first_month, dark_purple, second_month, this));
1712+ fake_data.insert("lois", data);
1713+ }
1714+
1715+ {
1716+ QVariantList first_month;
1717+ while (first_month.size() < 29)
1718+ first_month.push_back(QVariant(rand()));
1719+ while (first_month.size() < 31)
1720+ first_month.push_back(QVariant());
1721+ QVariantList second_month;
1722+ while (second_month.size() < 31)
1723+ second_month.push_back(QVariant(rand()));
1724+ QSharedPointer<InfographicData> data(
1725+ new InfographicData("<b>2</b>hrs watching videos today", red,
1726+ first_month, dark_purple, second_month, this));
1727+ fake_data.insert("toomas", data);
1728+ }
1729+
1730+ {
1731+ QVariantList first_month;
1732+ while (first_month.size() < 15)
1733+ first_month.push_back(QVariant(rand()));
1734+ while (first_month.size() < 31)
1735+ first_month.push_back(QVariant());
1736+ QVariantList second_month;
1737+ while (second_month.size() < 31)
1738+ second_month.push_back(QVariant(rand()));
1739+ QSharedPointer<InfographicData> data(
1740+ new InfographicData("<b>6</b>hrs watching videos today", red,
1741+ first_month, dark_purple, second_month, this));
1742+ fake_data.insert("empty-name", data);
1743+ }
1744+}
1745+
1746+void InfographicModelPrivate::setFakeData(int new_index)
1747+{
1748+ std::cout << "Data sets for user " << username.toStdString() << " = "
1749+ << fake_data.values(username).size() << ", index = " << new_index
1750+ << std::endl;
1751+
1752+ QSharedPointer<InfographicData> new_data;
1753+
1754+ if (!fake_data.contains(username))
1755+ {
1756+ data_index = 0;
1757+ new_data = empty_data;
1758+ } else
1759+ {
1760+ data_index = new_index;
1761+ new_data = fake_data.values(username).at(data_index);
1762+ }
1763+
1764+ data.label = new_data->label;
1765+ data.first_color = new_data->first_color;
1766+ data.first_month.setVariantList(new_data->first_month.variantList());
1767+ data.second_color = new_data->second_color;
1768+ data.second_month.setVariantList(new_data->second_month.variantList());
1769+
1770+ QVariantListModel &first_month(data.first_month);
1771+
1772+ for (int i(first_month.rowCount()); i != 0; --i)
1773+ {
1774+ if (!first_month.data(first_month.index(i), Qt::DisplayRole).isNull())
1775+ {
1776+ current_day = i;
1777+ break;
1778+ }
1779+
1780+ }
1781+
1782+ q_ptr->labelChanged(data.label);
1783+ q_ptr->firstColorChanged(data.first_color);
1784+ q_ptr->secondColorChanged(data.second_color);
1785+ q_ptr->currentDayChanged(current_day);
1786+}
1787+
1788+void InfographicModelPrivate::nextFakeData()
1789+{
1790+ if (!fake_data.contains(username))
1791+ {
1792+ return;
1793+ }
1794+
1795+ setFakeData((data_index + 1) % fake_data.values(username).size());
1796+}
1797+
1798+InfographicModel::InfographicModel(QObject *parent) :
1799+ QObject(parent), d_ptr(new InfographicModelPrivate(this))
1800+{
1801+}
1802+
1803+InfographicModel::~InfographicModel()
1804+{
1805+ delete d_ptr;
1806+}
1807+
1808+QString InfographicModel::label() const
1809+{
1810+ return d_ptr->data.label;
1811+}
1812+
1813+QString InfographicModel::username() const
1814+{
1815+ return d_ptr->username;
1816+}
1817+
1818+void InfographicModel::setUsername(const QString &username)
1819+{
1820+ d_ptr->username = username;
1821+ d_ptr->setFakeData(0);
1822+ usernameChanged(d_ptr->username);
1823+}
1824+
1825+QColor InfographicModel::firstColor() const
1826+{
1827+ return d_ptr->data.first_color;
1828+}
1829+
1830+QColor InfographicModel::secondColor() const
1831+{
1832+ return d_ptr->data.second_color;
1833+}
1834+
1835+QAbstractItemModel * InfographicModel::firstMonth() const
1836+{
1837+ return &d_ptr->data.first_month;
1838+}
1839+
1840+QAbstractItemModel * InfographicModel::secondMonth() const
1841+{
1842+ return &d_ptr->data.second_month;
1843+}
1844+
1845+int InfographicModel::currentDay() const
1846+{
1847+ return d_ptr->current_day;
1848+}
1849+
1850+void InfographicModel::nextDataSource()
1851+{
1852+ d_ptr->nextFakeData();
1853+}
1854+#include "lightdm-infographicmodel.moc"
1855+
1856
1857=== added file 'plugins/LightDM/lightdm-infographicmodel.h'
1858--- plugins/LightDM/lightdm-infographicmodel.h 1970-01-01 00:00:00 +0000
1859+++ plugins/LightDM/lightdm-infographicmodel.h 2013-05-10 12:09:35 +0000
1860@@ -0,0 +1,95 @@
1861+/*
1862+ * Copyright (C) 2013 Canonical, Ltd.
1863+ * Copyright (C) 2010-2011 David Edmundson.
1864+ *
1865+ * This program is free software; you can redistribute it and/or modify
1866+ * it under the terms of the GNU General Public License as published by
1867+ * the Free Software Foundation; version 3.
1868+ *
1869+ * This program is distributed in the hope that it will be useful,
1870+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1871+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1872+ * GNU General Public License for more details.
1873+ *
1874+ * You should have received a copy of the GNU General Public License
1875+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1876+ *
1877+ * Author: Pete Woods <pete.woods@canonical.com>
1878+ */
1879+
1880+#ifndef LIGHTDM_INFOGRAPHICMODEL_H
1881+#define LIGHTDM_INFOGRAPHICMODEL_H
1882+
1883+#include <QtCore/QString>
1884+#include <QtGui/qcolor.h>
1885+#include <QtCore/QSharedDataPointer>
1886+#include <QAbstractListModel>
1887+
1888+#include "qvariantlistmodel.h"
1889+
1890+namespace QLightDM
1891+{
1892+class InfographicModelPrivate;
1893+
1894+class Q_DECL_EXPORT InfographicModel : public QObject
1895+{
1896+ Q_OBJECT
1897+
1898+ Q_PROPERTY(QString label READ label NOTIFY labelChanged FINAL)
1899+ Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged FINAL)
1900+ Q_PROPERTY(QColor firstColor READ firstColor NOTIFY firstColorChanged FINAL)
1901+ Q_PROPERTY(QColor secondColor READ secondColor NOTIFY secondColorChanged FINAL)
1902+ Q_PROPERTY(QAbstractItemModel *firstMonth READ firstMonth NOTIFY firstMonthChanged FINAL)
1903+ Q_PROPERTY(QAbstractItemModel *secondMonth READ secondMonth NOTIFY secondMonthChanged FINAL)
1904+ Q_PROPERTY(int currentDay READ currentDay NOTIFY currentDayChanged FINAL)
1905+
1906+public:
1907+ explicit InfographicModel(QObject *parent = 0);
1908+ ~InfographicModel();
1909+
1910+ QString label() const;
1911+
1912+ QString username() const;
1913+
1914+ void setUsername(const QString &username);
1915+
1916+ QColor firstColor() const;
1917+
1918+ QAbstractItemModel *firstMonth() const;
1919+
1920+ int currentDay() const;
1921+
1922+ QColor secondColor() const;
1923+
1924+ QAbstractItemModel *secondMonth() const;
1925+
1926+Q_SIGNALS:
1927+ void labelChanged(const QString &label);
1928+
1929+ void usernameChanged(const QString &username);
1930+
1931+ void firstColorChanged(const QColor &color);
1932+
1933+ void firstMonthChanged(QAbstractItemModel *first_month);
1934+
1935+ void currentDayChanged(int length);
1936+
1937+ void secondColorChanged(const QColor &color);
1938+
1939+ void secondMonthChanged(QAbstractItemModel *second_month);
1940+
1941+public Q_SLOTS:
1942+ void nextDataSource();
1943+
1944+protected:
1945+
1946+private:
1947+ InfographicModelPrivate * const d_ptr;
1948+
1949+ Q_DECLARE_PRIVATE(InfographicModel)
1950+
1951+};
1952+
1953+}
1954+
1955+#endif // LIGHTDM_INFOGRAPHICMODEL_H
1956
1957=== added file 'plugins/LightDM/lightdm-usersmodel.cpp'
1958--- plugins/LightDM/lightdm-usersmodel.cpp 1970-01-01 00:00:00 +0000
1959+++ plugins/LightDM/lightdm-usersmodel.cpp 2013-05-10 12:09:35 +0000
1960@@ -0,0 +1,166 @@
1961+/*
1962+ * Copyright (C) 2013 Canonical, Ltd.
1963+ *
1964+ * This program is free software; you can redistribute it and/or modify
1965+ * it under the terms of the GNU General Public License as published by
1966+ * the Free Software Foundation; version 3.
1967+ *
1968+ * This program is distributed in the hope that it will be useful,
1969+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1970+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1971+ * GNU General Public License for more details.
1972+ *
1973+ * You should have received a copy of the GNU General Public License
1974+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1975+ *
1976+ * Author: Michael Terry <michael.terry@canonical.com>
1977+ */
1978+
1979+// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
1980+// To use the same method of setting role name that it does, we
1981+// set our compatibility to Qt4 here too.
1982+#define QT_DISABLE_DEPRECATED_BEFORE QT_VERSION_CHECK(4, 0, 0)
1983+
1984+#include "lightdm-usersmodel.h"
1985+#include <QtCore/QDebug>
1986+#include <QtCore/QDir>
1987+#include <QtCore/QString>
1988+#include <QtGui/QIcon>
1989+
1990+using namespace QLightDM;
1991+
1992+class Entry
1993+{
1994+public:
1995+ QString username;
1996+ QString real_name;
1997+ QString background;
1998+ QString layouts;
1999+ bool is_active;
2000+ bool has_messages;
2001+ QString session;
2002+};
2003+
2004+const Entry fake_entries[] =
2005+{
2006+ { "anna", "Anna Olson", "*", 0, false, false, 0 },
2007+ { "lois", "Lois Mcqueen", "*", 0, false, false, 0 },
2008+ { "lola", "Lola Chang", "*", 0, false, false, 0 },
2009+ { "toomas", "Toomas Vilms", "*", 0, false, false, 0 },
2010+ { "empty-name", "", "*", 0, false, false, 0 },
2011+ { "no-password", "Guest", "*", 0, false, false, 0 },
2012+ { "", "", 0, 0, false, false, 0 }
2013+};
2014+
2015+namespace QLightDM
2016+{
2017+class UsersModelPrivate
2018+{
2019+public:
2020+ explicit UsersModelPrivate(UsersModel *parent);
2021+ QList<Entry> entries;
2022+
2023+protected:
2024+ UsersModel * const q_ptr;
2025+ void fillEntries();
2026+
2027+private:
2028+ Q_DECLARE_PUBLIC(UsersModel)
2029+};
2030+}
2031+
2032+UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
2033+ : q_ptr(parent)
2034+{
2035+}
2036+
2037+void UsersModelPrivate::fillEntries()
2038+{
2039+ QDir bgdir = QDir("/usr/share/demo-assets/shell/backgrounds/");
2040+ QStringList backgrounds = bgdir.entryList(QDir::Files | QDir::NoDotAndDotDot);
2041+
2042+ for (int i = 0, j = 0; fake_entries[i].username != ""; i++) {
2043+ Entry entry = fake_entries[i];
2044+ if (entry.background == "*") {
2045+ if (backgrounds.isEmpty()) {
2046+ entry.background = ""; // to avoid warnings about a 404 on "*"
2047+ } else {
2048+ entry.background = bgdir.filePath(backgrounds[j++]);
2049+ if (j >= backgrounds.length()) {
2050+ j = 0;
2051+ }
2052+ }
2053+ }
2054+ entries.append(entry);
2055+ }
2056+}
2057+
2058+UsersModel::UsersModel(QObject *parent) :
2059+ QAbstractListModel(parent),
2060+ d_ptr(new UsersModelPrivate(this))
2061+{
2062+ Q_D(UsersModel);
2063+ // Extend roleNames (we want to keep the "display" role)
2064+ QHash<int, QByteArray> roles = roleNames();
2065+ roles[NameRole] = "name";
2066+ roles[RealNameRole] = "realName";
2067+ roles[LoggedInRole] = "loggedIn";
2068+ roles[BackgroundRole] = "background";
2069+ roles[BackgroundPathRole] = "backgroundPath";
2070+ roles[SessionRole] = "session";
2071+ roles[HasMessagesRole] = "hasMessages";
2072+ roles[ImagePathRole] = "imagePath";
2073+ setRoleNames(roles);
2074+ d->fillEntries();
2075+}
2076+
2077+UsersModel::~UsersModel()
2078+{
2079+ delete d_ptr;
2080+}
2081+
2082+int UsersModel::rowCount(const QModelIndex &parent) const
2083+{
2084+ Q_D(const UsersModel);
2085+
2086+ if (parent.isValid()) {
2087+ return 0;
2088+ } else { // parent is root
2089+ return d->entries.size();
2090+ }
2091+}
2092+
2093+QVariant UsersModel::data(const QModelIndex &index, int role) const
2094+{
2095+ Q_D(const UsersModel);
2096+
2097+ if (!index.isValid()) {
2098+ return QVariant();
2099+ }
2100+
2101+ int row = index.row();
2102+ switch (role) {
2103+ case Qt::DisplayRole:
2104+ return d->entries[row].real_name;
2105+ case Qt::DecorationRole:
2106+ return QIcon();
2107+ case UsersModel::NameRole:
2108+ return d->entries[row].username;
2109+ case UsersModel::RealNameRole:
2110+ return d->entries[row].real_name;
2111+ case UsersModel::SessionRole:
2112+ return d->entries[row].session;
2113+ case UsersModel::LoggedInRole:
2114+ return d->entries[row].is_active;
2115+ case UsersModel::BackgroundRole:
2116+ return QPixmap(d->entries[row].background);
2117+ case UsersModel::BackgroundPathRole:
2118+ return d->entries[row].background;
2119+ case UsersModel::HasMessagesRole:
2120+ return d->entries[row].has_messages;
2121+ case UsersModel::ImagePathRole:
2122+ return "";
2123+ default:
2124+ return QVariant();
2125+ }
2126+}
2127
2128=== added file 'plugins/LightDM/lightdm-usersmodel.h'
2129--- plugins/LightDM/lightdm-usersmodel.h 1970-01-01 00:00:00 +0000
2130+++ plugins/LightDM/lightdm-usersmodel.h 2013-05-10 12:09:35 +0000
2131@@ -0,0 +1,66 @@
2132+/*
2133+ * Copyright (C) 2013 Canonical, Ltd.
2134+ * Copyright (C) 2010-2011 David Edmundson.
2135+ *
2136+ * This program is free software; you can redistribute it and/or modify
2137+ * it under the terms of the GNU General Public License as published by
2138+ * the Free Software Foundation; version 3.
2139+ *
2140+ * This program is distributed in the hope that it will be useful,
2141+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2142+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2143+ * GNU General Public License for more details.
2144+ *
2145+ * You should have received a copy of the GNU General Public License
2146+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2147+ *
2148+ * Author: David Edmundson <kde@davidedmundson.co.uk>
2149+ */
2150+
2151+#ifndef LIGHTDM_USERSMODEL_H
2152+#define LIGHTDM_USERSMODEL_H
2153+
2154+#include <QtCore/QString>
2155+#include <QtCore/QSharedDataPointer>
2156+#include <QAbstractListModel>
2157+
2158+
2159+namespace QLightDM
2160+{
2161+class UsersModelPrivate;
2162+
2163+class Q_DECL_EXPORT UsersModel : public QAbstractListModel
2164+{
2165+ Q_OBJECT
2166+
2167+ Q_ENUMS(UserModelRoles)
2168+
2169+public:
2170+ explicit UsersModel(QObject *parent = 0);
2171+ ~UsersModel();
2172+
2173+ enum UserModelRoles {NameRole = Qt::UserRole,
2174+ RealNameRole,
2175+ LoggedInRole,
2176+ BackgroundRole,
2177+ SessionRole,
2178+ HasMessagesRole,
2179+ ImagePathRole,
2180+ BackgroundPathRole
2181+ };
2182+
2183+ int rowCount(const QModelIndex &parent) const;
2184+ QVariant data(const QModelIndex &index, int role) const;
2185+
2186+protected:
2187+
2188+private:
2189+ UsersModelPrivate * const d_ptr;
2190+
2191+ Q_DECLARE_PRIVATE(UsersModel)
2192+
2193+};
2194+
2195+}
2196+
2197+#endif // LIGHTDM_USERSMODEL_H
2198
2199=== added file 'plugins/LightDM/plugin.cpp'
2200--- plugins/LightDM/plugin.cpp 1970-01-01 00:00:00 +0000
2201+++ plugins/LightDM/plugin.cpp 2013-05-10 12:09:35 +0000
2202@@ -0,0 +1,57 @@
2203+/*
2204+ * Copyright (C) 2012,2013 Canonical, Ltd.
2205+ *
2206+ * This program is free software; you can redistribute it and/or modify
2207+ * it under the terms of the GNU General Public License as published by
2208+ * the Free Software Foundation; version 3.
2209+ *
2210+ * This program is distributed in the hope that it will be useful,
2211+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2212+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2213+ * GNU General Public License for more details.
2214+ *
2215+ * You should have received a copy of the GNU General Public License
2216+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2217+ *
2218+ * Authors: Gerry Boland <gerry.boland@canonical.com>
2219+ * Michael Terry <michael.terry@canonical.com>
2220+ */
2221+
2222+#include "plugin.h"
2223+#include "greeter.h"
2224+#include "lightdm-usersmodel.h"
2225+#include "usersmodel.h"
2226+#include "infographicmodel.h"
2227+#include <QtCore/QAbstractItemModel>
2228+#include <QtQml>
2229+
2230+static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2231+{
2232+ Q_UNUSED(engine)
2233+ Q_UNUSED(scriptEngine)
2234+ return new Greeter();
2235+}
2236+
2237+static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2238+{
2239+ Q_UNUSED(engine)
2240+ Q_UNUSED(scriptEngine)
2241+ return new UsersModel();
2242+}
2243+
2244+static QObject *infographic_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2245+{
2246+ Q_UNUSED(engine)
2247+ Q_UNUSED(scriptEngine)
2248+ return new InfographicModel();
2249+}
2250+
2251+void LightDMPlugin::registerTypes(const char *uri)
2252+{
2253+ Q_ASSERT(uri == QLatin1String("LightDM"));
2254+ qmlRegisterSingletonType<Greeter>(uri, 0, 1, "Greeter", greeter_provider);
2255+ qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
2256+ qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
2257+ qmlRegisterSingletonType<InfographicModel>(uri, 0, 1, "Infographic", infographic_provider);
2258+}
2259+
2260
2261=== added file 'plugins/LightDM/plugin.h'
2262--- plugins/LightDM/plugin.h 1970-01-01 00:00:00 +0000
2263+++ plugins/LightDM/plugin.h 2013-05-10 12:09:35 +0000
2264@@ -0,0 +1,35 @@
2265+/*
2266+ * Copyright (C) 2012,2013 Canonical, Ltd.
2267+ *
2268+ * This program is free software; you can redistribute it and/or modify
2269+ * it under the terms of the GNU General Public License as published by
2270+ * the Free Software Foundation; version 3.
2271+ *
2272+ * This program is distributed in the hope that it will be useful,
2273+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2274+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2275+ * GNU General Public License for more details.
2276+ *
2277+ * You should have received a copy of the GNU General Public License
2278+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2279+ *
2280+ * Authors: Gerry Boland <gerry.boland@canonical.com>
2281+ * Michael Terry <michael.terry@canonical.com>
2282+ */
2283+
2284+#ifndef LIGHTDM_PLUGIN_H
2285+#define LIGHTDM_PLUGIN_H
2286+
2287+#include <QtQml/QQmlEngine>
2288+#include <QtQml/QQmlExtensionPlugin>
2289+
2290+class LightDMPlugin : public QQmlExtensionPlugin
2291+{
2292+ Q_OBJECT
2293+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2294+
2295+public:
2296+ void registerTypes(const char *uri);
2297+};
2298+
2299+#endif
2300
2301=== added file 'plugins/LightDM/qmldir'
2302--- plugins/LightDM/qmldir 1970-01-01 00:00:00 +0000
2303+++ plugins/LightDM/qmldir 2013-05-10 12:09:35 +0000
2304@@ -0,0 +1,2 @@
2305+module LightDM
2306+plugin LightDM-qml
2307
2308=== added file 'plugins/LightDM/qvariantlistmodel.cpp'
2309--- plugins/LightDM/qvariantlistmodel.cpp 1970-01-01 00:00:00 +0000
2310+++ plugins/LightDM/qvariantlistmodel.cpp 2013-05-10 12:09:35 +0000
2311@@ -0,0 +1,243 @@
2312+/*
2313+ A simple model that uses a QVariantList as its data source.
2314+*/
2315+
2316+// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
2317+// To use the same method of setting role name that it does, we
2318+// set our compatibility to Qt4 here too.
2319+#define QT_DISABLE_DEPRECATED_BEFORE QT_VERSION_CHECK(4, 0, 0)
2320+
2321+#include "qvariantlistmodel.h"
2322+
2323+#include <QtCore/qvector.h>
2324+
2325+
2326+/*!
2327+ \class QVariantListModel
2328+ \inmodule QtCore
2329+ \brief The QVariantListModel class provides a model that supplies strings to views.
2330+
2331+ \ingroup model-view
2332+
2333+ QVariantListModel is an editable model that can be used for simple
2334+ cases where you need to display a number of strings in a view
2335+ widget, such as a QListView or a QComboBox.
2336+
2337+ The model provides all the standard functions of an editable
2338+ model, representing the data in the string list as a model with
2339+ one column and a number of rows equal to the number of items in
2340+ the list.
2341+
2342+ Model indexes corresponding to items are obtained with the
2343+ \l{QAbstractListModel::index()}{index()} function, and item flags
2344+ are obtained with flags(). Item data is read with the data()
2345+ function and written with setData(). The number of rows (and
2346+ number of items in the string list) can be found with the
2347+ rowCount() function.
2348+
2349+ The model can be constructed with an existing string list, or
2350+ strings can be set later with the setStringList() convenience
2351+ function. Strings can also be inserted in the usual way with the
2352+ insertRows() function, and removed with removeRows(). The contents
2353+ of the string list can be retrieved with the stringList()
2354+ convenience function.
2355+
2356+ An example usage of QVariantListModel:
2357+
2358+ \snippet qvariantlistmodel/main.cpp 0
2359+
2360+ \sa QAbstractListModel, QAbstractItemModel, {Model Classes}
2361+*/
2362+
2363+/*!
2364+ Constructs a variant list model with the given \a parent.
2365+*/
2366+
2367+QVariantListModel::QVariantListModel(QObject *parent)
2368+ : QAbstractListModel(parent)
2369+{
2370+ QHash<int, QByteArray> roles(roleNames());
2371+ roles[Qt::DisplayRole] = "modelData";
2372+ setRoleNames(roles);
2373+}
2374+
2375+/*!
2376+ Constructs a variant list model containing the specified \a list
2377+ with the given \a parent.
2378+*/
2379+
2380+QVariantListModel::QVariantListModel(const QVariantList &list, QObject *parent)
2381+ : QAbstractListModel(parent), lst(list)
2382+{
2383+ QHash<int, QByteArray> roles(roleNames());
2384+ roles[Qt::DisplayRole] = "modelData";
2385+ setRoleNames(roles);
2386+}
2387+
2388+/*!
2389+ Returns the number of rows in the model. This value corresponds to the
2390+ number of items in the model's internal variant list.
2391+
2392+ The optional \a parent argument is in most models used to specify
2393+ the parent of the rows to be counted. Because this is a list if a
2394+ valid parent is specified, the result will always be 0.
2395+
2396+ \sa insertRows(), removeRows(), QAbstractItemModel::rowCount()
2397+*/
2398+
2399+int QVariantListModel::rowCount(const QModelIndex &parent) const
2400+{
2401+ if (parent.isValid())
2402+ return 0;
2403+
2404+ return lst.count();
2405+}
2406+
2407+/*!
2408+ \reimp
2409+*/
2410+QModelIndex QVariantListModel::sibling(int row, int column, const QModelIndex &idx) const
2411+{
2412+ if (!idx.isValid() || column != 0 || row >= lst.count())
2413+ return QModelIndex();
2414+
2415+ return createIndex(row, 0);
2416+}
2417+
2418+/*!
2419+ Returns data for the specified \a role, from the item with the
2420+ given \a index.
2421+
2422+ If the view requests an invalid index, an invalid variant is returned.
2423+
2424+ \sa setData()
2425+*/
2426+
2427+QVariant QVariantListModel::data(const QModelIndex &index, int role) const
2428+{
2429+ if (index.row() < 0 || index.row() >= lst.size())
2430+ return QVariant();
2431+
2432+ if (role == Qt::DisplayRole || role == Qt::EditRole)
2433+ return lst.at(index.row());
2434+
2435+ return QVariant();
2436+}
2437+
2438+/*!
2439+ Returns the flags for the item with the given \a index.
2440+
2441+ Valid items are enabled, selectable, editable, drag enabled and drop enabled.
2442+
2443+ \sa QAbstractItemModel::flags()
2444+*/
2445+
2446+Qt::ItemFlags QVariantListModel::flags(const QModelIndex &index) const
2447+{
2448+ if (!index.isValid())
2449+ return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;
2450+
2451+ return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
2452+}
2453+
2454+/*!
2455+ Sets the data for the specified \a role in the item with the given
2456+ \a index in the model, to the provided \a value.
2457+
2458+ The dataChanged() signal is emitted if the item is changed.
2459+
2460+ \sa Qt::ItemDataRole, data()
2461+*/
2462+
2463+bool QVariantListModel::setData(const QModelIndex &index, const QVariant &value, int role)
2464+{
2465+ if (index.row() >= 0 && index.row() < lst.size()
2466+ && (role == Qt::EditRole || role == Qt::DisplayRole)) {
2467+ lst.replace(index.row(), value);
2468+ dataChanged(index, index, QVector<int>() << role);
2469+ return true;
2470+ }
2471+ return false;
2472+}
2473+
2474+/*!
2475+ Inserts \a count rows into the model, beginning at the given \a row.
2476+
2477+ The \a parent index of the rows is optional and is only used for
2478+ consistency with QAbstractItemModel. By default, a null index is
2479+ specified, indicating that the rows are inserted in the top level of
2480+ the model.
2481+
2482+ \sa QAbstractItemModel::insertRows()
2483+*/
2484+
2485+bool QVariantListModel::insertRows(int row, int count, const QModelIndex &parent)
2486+{
2487+ if (count < 1 || row < 0 || row > rowCount(parent))
2488+ return false;
2489+
2490+ beginInsertRows(QModelIndex(), row, row + count - 1);
2491+
2492+ for (int r = 0; r < count; ++r)
2493+ lst.insert(row, QVariant());
2494+
2495+ endInsertRows();
2496+
2497+ return true;
2498+}
2499+
2500+/*!
2501+ Removes \a count rows from the model, beginning at the given \a row.
2502+
2503+ The \a parent index of the rows is optional and is only used for
2504+ consistency with QAbstractItemModel. By default, a null index is
2505+ specified, indicating that the rows are removed in the top level of
2506+ the model.
2507+
2508+ \sa QAbstractItemModel::removeRows()
2509+*/
2510+
2511+bool QVariantListModel::removeRows(int row, int count, const QModelIndex &parent)
2512+{
2513+ if (count <= 0 || row < 0 || (row + count) > rowCount(parent))
2514+ return false;
2515+
2516+ beginRemoveRows(QModelIndex(), row, row + count - 1);
2517+
2518+ for (int r = 0; r < count; ++r)
2519+ lst.removeAt(row);
2520+
2521+ endRemoveRows();
2522+
2523+ return true;
2524+}
2525+
2526+/*!
2527+ Returns the variant list used by the model to store data.
2528+*/
2529+QVariantList QVariantListModel::variantList() const
2530+{
2531+ return lst;
2532+}
2533+
2534+/*!
2535+ Sets the model's internal variant list to \a list. The model will
2536+ notify any attached views that its underlying data has changed.
2537+
2538+ \sa dataChanged()
2539+*/
2540+void QVariantListModel::setVariantList(const QVariantList &list)
2541+{
2542+ beginResetModel();
2543+ lst = list;
2544+ endResetModel();
2545+}
2546+
2547+/*!
2548+ \reimp
2549+*/
2550+Qt::DropActions QVariantListModel::supportedDropActions() const
2551+{
2552+ return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
2553+}
2554+
2555
2556=== added file 'plugins/LightDM/qvariantlistmodel.h'
2557--- plugins/LightDM/qvariantlistmodel.h 1970-01-01 00:00:00 +0000
2558+++ plugins/LightDM/qvariantlistmodel.h 2013-05-10 12:09:35 +0000
2559@@ -0,0 +1,57 @@
2560+/*
2561+ * Copyright (C) 2013 Canonical, Ltd.
2562+ * Copyright (C) 2010-2011 David Edmundson.
2563+ *
2564+ * This program is free software; you can redistribute it and/or modify
2565+ * it under the terms of the GNU General Public License as published by
2566+ * the Free Software Foundation; version 3.
2567+ *
2568+ * This program is distributed in the hope that it will be useful,
2569+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2570+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2571+ * GNU General Public License for more details.
2572+ *
2573+ * You should have received a copy of the GNU General Public License
2574+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2575+ *
2576+ * Author: Pete Woods <pete.woods@canonical.com>
2577+ */
2578+
2579+#ifndef QVARIANTLISTMODEL_H
2580+#define QVARIANTLISTMODEL_H
2581+
2582+#include <QAbstractListModel>
2583+#include <QVariantList>
2584+
2585+class Q_CORE_EXPORT QVariantListModel : public QAbstractListModel
2586+{
2587+ Q_OBJECT
2588+public:
2589+ explicit QVariantListModel(QObject *parent = 0);
2590+ explicit QVariantListModel(const QVariantList &list, QObject *parent = 0);
2591+
2592+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
2593+ QModelIndex sibling(int row, int column, const QModelIndex &idx) const;
2594+
2595+ QVariant data(const QModelIndex &index, int role) const;
2596+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
2597+
2598+ Qt::ItemFlags flags(const QModelIndex &index) const;
2599+
2600+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
2601+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
2602+
2603+ //void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
2604+
2605+ QVariantList variantList() const;
2606+ void setVariantList(const QVariantList &list);
2607+
2608+ Qt::DropActions supportedDropActions() const;
2609+
2610+private:
2611+ Q_DISABLE_COPY(QVariantListModel)
2612+ QVariantList lst;
2613+};
2614+
2615+
2616+#endif // QVARIANTLISTMODEL_H
2617
2618=== added file 'plugins/LightDM/usersmodel.cpp'
2619--- plugins/LightDM/usersmodel.cpp 1970-01-01 00:00:00 +0000
2620+++ plugins/LightDM/usersmodel.cpp 2013-05-10 12:09:35 +0000
2621@@ -0,0 +1,68 @@
2622+/*
2623+ * Copyright (C) 2013 Canonical, Ltd.
2624+ *
2625+ * This program is free software; you can redistribute it and/or modify
2626+ * it under the terms of the GNU General Public License as published by
2627+ * the Free Software Foundation; version 3.
2628+ *
2629+ * This program is distributed in the hope that it will be useful,
2630+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2631+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2632+ * GNU General Public License for more details.
2633+ *
2634+ * You should have received a copy of the GNU General Public License
2635+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2636+ *
2637+ * Author: Michael Terry <michael.terry@canonical.com>
2638+ */
2639+
2640+#include "usersmodel.h"
2641+#include "lightdm-usersmodel.h"
2642+#include <QtCore/QSortFilterProxyModel>
2643+
2644+// First, we define an internal class that wraps LightDM's UsersModel. This
2645+// class will modify some of the data coming from LightDM. For example, we
2646+// modify any empty Real Names into just normal Names.
2647+// (We can't modify the data directly in UsersModel below because it won't sort
2648+// using the modified data.)
2649+class MangleModel : public QSortFilterProxyModel
2650+{
2651+ Q_OBJECT
2652+
2653+public:
2654+ explicit MangleModel(QObject* parent=0);
2655+
2656+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
2657+};
2658+
2659+MangleModel::MangleModel(QObject* parent)
2660+ : QSortFilterProxyModel(parent)
2661+{
2662+ setSourceModel(new QLightDM::UsersModel(this));
2663+}
2664+
2665+QVariant MangleModel::data(const QModelIndex &index, int role) const
2666+{
2667+ QVariant data = QSortFilterProxyModel::data(index, role);
2668+
2669+ // If user's real name is empty, switch to unix name
2670+ if (role == QLightDM::UsersModel::RealNameRole && data.toString().isEmpty()) {
2671+ data = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
2672+ }
2673+
2674+ return data;
2675+}
2676+
2677+// **** Now we continue with actual UsersModel class ****
2678+
2679+UsersModel::UsersModel(QObject* parent)
2680+ : QSortFilterProxyModelQML(parent)
2681+{
2682+ setModel(new MangleModel(this));
2683+ setSortCaseSensitivity(Qt::CaseInsensitive);
2684+ setSortLocaleAware(true);
2685+ setSortRole(QLightDM::UsersModel::RealNameRole);
2686+ sort(0);
2687+}
2688+
2689+#include "usersmodel.moc"
2690
2691=== added file 'plugins/LightDM/usersmodel.h'
2692--- plugins/LightDM/usersmodel.h 1970-01-01 00:00:00 +0000
2693+++ plugins/LightDM/usersmodel.h 2013-05-10 12:09:35 +0000
2694@@ -0,0 +1,37 @@
2695+/*
2696+ * Copyright (C) 2012,2013 Canonical, Ltd.
2697+ *
2698+ * This program is free software; you can redistribute it and/or modify
2699+ * it under the terms of the GNU General Public License as published by
2700+ * the Free Software Foundation; version 3.
2701+ *
2702+ * This program is distributed in the hope that it will be useful,
2703+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2704+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2705+ * GNU General Public License for more details.
2706+ *
2707+ * You should have received a copy of the GNU General Public License
2708+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2709+ *
2710+ * Authors: Michael Terry <michael.terry@canonical.com>
2711+ */
2712+
2713+/* This class is a really tiny filter around QLightDM::UsersModel. There are
2714+ some operations that we want to edit a bit for the benefit of Qml.
2715+ Specifically, we want to sort users according to realName. */
2716+
2717+#ifndef USERSMODEL_H
2718+#define USERSMODEL_H
2719+
2720+#include "plugins/Utils/qsortfilterproxymodelqml.h"
2721+#include <QtCore/QObject>
2722+
2723+class UsersModel : public QSortFilterProxyModelQML
2724+{
2725+ Q_OBJECT
2726+
2727+public:
2728+ explicit UsersModel(QObject* parent=0);
2729+};
2730+
2731+#endif
2732
2733=== modified file 'plugins/Utils/qsortfilterproxymodelqml.cpp'
2734--- plugins/Utils/qsortfilterproxymodelqml.cpp 2013-05-09 14:23:10 +0000
2735+++ plugins/Utils/qsortfilterproxymodelqml.cpp 2013-05-10 12:09:35 +0000
2736@@ -57,6 +57,16 @@
2737 }
2738 }
2739
2740+QVariant
2741+QSortFilterProxyModelQML::data(int row, int role)
2742+{
2743+ if (sourceModel() == NULL) {
2744+ return QVariant();
2745+ }
2746+
2747+ return index(row, 0).data(role);
2748+}
2749+
2750 int
2751 QSortFilterProxyModelQML::totalCount() const
2752 {
2753
2754=== modified file 'plugins/Utils/qsortfilterproxymodelqml.h'
2755--- plugins/Utils/qsortfilterproxymodelqml.h 2013-05-08 11:22:15 +0000
2756+++ plugins/Utils/qsortfilterproxymodelqml.h 2013-05-10 12:09:35 +0000
2757@@ -31,6 +31,7 @@
2758 public:
2759 explicit QSortFilterProxyModelQML(QObject *parent = 0);
2760
2761+ Q_INVOKABLE QVariant data(int row, int role);
2762 Q_INVOKABLE int count();
2763 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
2764 virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
2765
2766=== modified file 'tests/autopilot/qml_phone_shell/tests/helpers.py'
2767--- tests/autopilot/qml_phone_shell/tests/helpers.py 2013-04-05 11:03:11 +0000
2768+++ tests/autopilot/qml_phone_shell/tests/helpers.py 2013-05-10 12:09:35 +0000
2769@@ -15,26 +15,49 @@
2770 class TestShellHelpers(object):
2771 """Helpers for testing the Shell"""
2772
2773- def unlock_greeter(self, retries=2):
2774+ def select_greeter_user(self, username):
2775 greeter = self.main_window.get_greeter()
2776 self.assertThat(greeter.created, Eventually(Equals(True)))
2777-
2778- if greeter.multiUser:
2779- login_loader = self.main_window.get_login_loader()
2780- self.assertThat(login_loader.progress, Eventually(Equals(1)))
2781- login_list = self.main_window.get_login_list()
2782- path_view = login_list.get_children_by_type("QQuickPathView")[0]
2783+ self.assertThat(greeter.multiUser, Eventually(Equals(True)))
2784+
2785+ login_loader = self.main_window.get_login_loader()
2786+ self.assertThat(login_loader.progress, Eventually(Equals(1)))
2787+
2788+ login_list = self.main_window.get_login_list()
2789+ path_view = login_list.get_children_by_type("QQuickPathView")[0]
2790+
2791+ try_count = 0
2792+ max_tries = 50 # just in case we go off rails
2793+ while try_count < max_tries:
2794 users = path_view.get_children_by_type("QQuickItem")
2795+ target_user = None
2796 for user in users:
2797 try:
2798 user_label = user.get_children_by_type("Label")[0]
2799- if user_label.text == "Guest":
2800- self.pointing_device.move_to_object(user_label)
2801- self.pointing_device.click()
2802- except:
2803+ if user_label.text == username:
2804+ target_user = user
2805+ break
2806+ elif target_user is None or user.y > target_user.y:
2807+ target_user = user
2808+ except Exception:
2809 pass
2810-
2811- password_field = login_list.get_children_by_type("TextField")[0]
2812+ if target_user is None:
2813+ break
2814+ user_label = target_user.get_children_by_type("Label")[0]
2815+ self.pointing_device.move_to_object(user_label)
2816+ self.pointing_device.click()
2817+ self.assertThat(path_view.movingInternally, Eventually(Equals(False)))
2818+ if user_label.text == username:
2819+ return login_list.get_children_by_type("TextField")[0]
2820+ try_count = try_count + 1
2821+ self.fail() # We didn't find it
2822+
2823+ def unlock_greeter(self, retries=2):
2824+ greeter = self.main_window.get_greeter()
2825+ self.assertThat(greeter.created, Eventually(Equals(True)))
2826+
2827+ if greeter.multiUser:
2828+ password_field = self.select_greeter_user("Guest")
2829 self.pointing_device.move_to_object(password_field)
2830 self.assertThat(password_field.opacity, Eventually(Equals(1)))
2831 self.pointing_device.click()
2832
2833=== modified file 'tests/qmltests/CMakeLists.txt'
2834--- tests/qmltests/CMakeLists.txt 2013-05-09 07:01:25 +0000
2835+++ tests/qmltests/CMakeLists.txt 2013-05-10 12:09:35 +0000
2836@@ -49,7 +49,7 @@
2837 ${CMAKE_BINARY_DIR}/tests/mocks)
2838 add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
2839 add_qml_test(Dash/People PeopleFilterGrid IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
2840-add_qml_test(Greeter Greeter)
2841+add_qml_test(Greeter Greeter IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
2842 add_qml_test(Hud Hud)
2843 add_qml_test(Hud Result)
2844 add_qml_test(Launcher Launcher)
2845@@ -60,4 +60,4 @@
2846 add_qml_test(Panel Panel)
2847 add_qml_test(Panel SearchIndicator)
2848 add_qml_test(Panel/Menus IndicatorMenuWindow IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS})
2849-add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
2850+add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
2851\ No newline at end of file
2852
2853=== modified file 'tests/qmltests/Greeter/tst_Greeter.qml'
2854--- tests/qmltests/Greeter/tst_Greeter.qml 2013-04-17 13:01:10 +0000
2855+++ tests/qmltests/Greeter/tst_Greeter.qml 2013-05-10 12:09:35 +0000
2856@@ -46,53 +46,69 @@
2857 name: "Greeter"
2858 when: windowShown
2859
2860+ function select_user(name) {
2861+ // We could be anywhere in list; find target index to know which direction
2862+ for (var i = 0; i < greeter.model.count; i++) {
2863+ if (greeter.model.get(i).name == name) {
2864+ break
2865+ }
2866+ }
2867+ if (i == greeter.model.count) {
2868+ return
2869+ }
2870+ var pathview = findChild(greeter, "userList")
2871+ while (pathview.currentIndex != i) {
2872+ var next = pathview.currentIndex + 1
2873+ if (pathview.currentIndex > i) {
2874+ next = pathview.currentIndex - 1
2875+ }
2876+ var account = findChild(greeter, "username"+next)
2877+ mouseClick(account, 1, 1)
2878+ tryCompare(pathview, "currentIndex", next)
2879+ }
2880+ }
2881+
2882 function test_cycle_data() {
2883- tryCompare(findChild(greeter, "userList"), "count", 5)
2884+ tryCompare(findChild(greeter, "userList"), "count", 6)
2885 var data = new Array()
2886 for (var i = 1; i < greeter.model.count; i++) {
2887 data[i] = {tag: greeter.model.get(i).name, username: "username"+i, uid: i }
2888 }
2889+ // We add first name last in data to guarantee a selected signal
2890 data[greeter.model.count] = {tag: greeter.model.get(0).name, username: "username0", uid: 0 }
2891 return data
2892 }
2893
2894 function test_cycle(data) {
2895- tryCompare(findChild(greeter, "userList"), "count", 5)
2896+ tryCompare(findChild(greeter, "userList"), "count", 6)
2897 selectionSpy.clear();
2898 var user = findChild(greeter, data.username)
2899 mouseClick(user, user.width / 2, user.height / 2)
2900 var pathview = findChild(greeter, "userList")
2901 tryCompare(pathview, "currentIndex", data.uid)
2902- tryCompare(greeter, "locked", greeter.model.get(data.uid).password !== undefined)
2903+ tryCompare(greeter, "locked", data.tag !== "no-password")
2904 tryCompare(selectionSpy, "count", 1)
2905 }
2906
2907 function test_unlock_password() {
2908+ select_user("lois") // to guarantee a selected signal
2909 unlockSpy.clear()
2910- // First one is Lolas account right now. Replace with password protected user from lightdm
2911- var account = findChild(greeter, "username1")
2912- mouseClick(account, 1, 1)
2913- account = findChild(greeter, "username0")
2914- mouseClick(account, 1, 1)
2915+ select_user("lola")
2916 var passwordInput = findChild(greeter, "passwordInput")
2917- tryCompare(passwordInput, "opacity", 0)
2918 tryCompare(passwordInput, "opacity", 1)
2919 mouseClick(passwordInput, 1, 1)
2920 compare(unlockSpy.count, 0)
2921- typeString(greeter.model.get(0).password)
2922+ typeString("password")
2923 keyClick(Qt.Key_Enter)
2924 unlockSpy.wait()
2925 }
2926
2927 function test_unlock_wrong_password() {
2928+ select_user("lois") // to guarantee a selected signal
2929 unlockSpy.clear()
2930- // First one is Lolas account right now. Replace with password protected user from lightdm
2931- var account = findChild(greeter, "username1")
2932- mouseClick(account, 1, 1)
2933- account = findChild(greeter, "username0")
2934- var passwordInput = findChild(greeter, "passwordInput")
2935- mouseClick(account, 1, 1)
2936+ select_user("lola")
2937 wait(0) // spin event loop to start any pending animations
2938+ var passwordInput = findChild(greeter, "passwordInput")
2939 tryCompare(passwordInput, "opacity", 1) // wait for opacity animation to be finished
2940 mouseClick(passwordInput, 1, 1)
2941 compare(unlockSpy.count, 0)
2942@@ -103,14 +119,23 @@
2943
2944 function test_unlock_no_password() {
2945 unlockSpy.clear()
2946- // Last one is the guest account for now. Replace with passwordless user from lightdm
2947- var guestAccount = findChild(greeter, "username" + (greeter.model.count - 1))
2948- mouseClick(guestAccount, guestAccount.width / 2, guestAccount.height / 2)
2949+ select_user("no-password")
2950 var passwordInput = findChild(greeter, "passwordInput")
2951 tryCompare(passwordInput, "opacity", 1)
2952 mouseClick(passwordInput, 1, 1)
2953 unlockSpy.wait()
2954 compare(unlockSpy.count, 1)
2955 }
2956+
2957+ function test_empty_name() {
2958+ for (var i = 0; i < greeter.model.count; i++) {
2959+ if (greeter.model.get(i).name == "empty-name") {
2960+ compare(greeter.model.get(i).realName, greeter.model.get(i).name)
2961+ return
2962+ }
2963+ }
2964+ // Never found empty-name...
2965+ throw new Error("QtQuickTest::fail")
2966+ }
2967 }
2968 }

Subscribers

People subscribed via source and target branches

to all changes: