2 * Copyright (C) 2013 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18import QtQuick.Window 2.0
19import AccountsService 0.1
21import Unity.Application 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.Popups 1.0
24import Ubuntu.Gestures 0.1
25import Ubuntu.SystemImage 0.1
26import Ubuntu.Telephony 0.1 as Telephony
27import Unity.Connectivity 0.1
28import Unity.Launcher 0.1
30import LightDM 0.1 as LightDM
32import SessionBroadcast 0.1
39import "Panel/Indicators"
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.DashCommunicator 0.1
47 // this is only here to select the width / height of the window if not running fullscreen
48 property bool tablet: false
49 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
50 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
52 property real edgeSize: units.gu(2)
53 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
54 property url background
55 readonly property real panelHeight: panel.panelHeight
57 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated
59 property bool sideStageEnabled: shell.width >= units.gu(100)
60 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
62 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
63 property int failedLoginsDelayAttempts: 7 // number of failed logins
64 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
66 property int orientation
67 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
68 onDeviceOrientationAngleChanged: {
69 if (!OrientationLock.enabled) {
70 orientation = Screen.orientation;
73 readonly property bool orientationLockEnabled: OrientationLock.enabled
74 onOrientationLockEnabledChanged: {
75 if (orientationLockEnabled) {
76 OrientationLock.savedOrientation = Screen.orientation;
78 orientation = Screen.orientation;
82 function activateApplication(appId) {
83 if (ApplicationManager.findApplication(appId)) {
84 ApplicationManager.requestFocusApplication(appId);
86 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
87 ApplicationManager.startApplication(appId, execFlags);
91 function setFakeActiveForApp(app) {
93 greeter.fakeActiveForApp = app
100 property: "applicationManager"
101 value: ApplicationManager
104 Component.onCompleted: {
105 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
106 if (ApplicationManager.count > 0) {
107 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
109 if (orientationLockEnabled) {
110 orientation = OrientationLock.savedOrientation;
115 id: backgroundSettings
116 schema.id: "org.gnome.desktop.background"
118 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
119 onGSettingsPictureChanged: {
120 shell.background = gSettingsPicture
129 objectName: "dashCommunicator"
133 // Handle but do not filter out volume keys
134 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
135 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
138 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
139 dialogs.onPowerKeyPressed();
140 event.accepted = true;
142 event.accepted = false;
147 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
148 dialogs.onPowerKeyReleased();
149 event.accepted = true;
151 event.accepted = false;
160 height: parent.height
161 visible: !ApplicationManager.empty
164 target: ApplicationManager
166 if (appId === "dialer-app") {
167 // Always let the dialer-app through. Either user asked
168 // for an emergency call or accepted an incoming call.
169 setFakeActiveForApp(appId)
170 } else if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
177 onFocusedApplicationIdChanged: {
178 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
181 panel.indicators.hide();
184 onApplicationAdded: {
185 if (greeter.shown && appId != "unity8-dash") {
188 if (appId === "dialer-app") {
189 // Always let the dialer-app through. Either user asked
190 // for an emergency call or accepted an incoming call.
191 setFakeActiveForApp(appId)
198 id: applicationsDisplayLoader
201 source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
204 target: applicationsDisplayLoader.item
205 property: "objectName"
209 target: applicationsDisplayLoader.item
210 property: "dragAreaWidth"
211 value: shell.edgeSize
214 target: applicationsDisplayLoader.item
215 property: "maximizedAppTopMargin"
216 // Not just using panel.panelHeight as that changes depending on the focused app.
217 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
220 target: applicationsDisplayLoader.item
221 property: "interactive"
222 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
225 target: applicationsDisplayLoader.item
226 property: "spreadEnabled"
227 value: edgeDemo.stagesEnabled && greeter.fakeActiveForApp === "" // to support emergency dialer hack
230 target: applicationsDisplayLoader.item
231 property: "inverseProgress"
232 value: launcher.progress
235 target: applicationsDisplayLoader.item
236 property: "orientation"
237 value: shell.orientation
244 objectName: "inputMethod"
245 anchors { fill: parent; topMargin: panel.panelHeight }
246 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
250 target: SurfaceManager
252 if (surface.type == MirSurfaceItem.InputMethod) {
253 inputMethod.surface = surface;
257 onSurfaceDestroyed: {
258 if (inputMethod.surface == surface) {
259 inputMethod.surface = null;
260 surface.parent = null;
262 if (!surface.parent) {
263 // there's no one displaying it. delete it right away
269 target: SessionManager
271 if (!session.parentSession && !session.application) {
272 // nothing is using it. delete it right away
280 objectName: "lockscreen"
282 hides: [launcher, panel.indicators]
285 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
286 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
290 height: parent.height - panel.panelHeight
291 background: shell.background
292 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
296 // FIXME: We *should* show emergency dialer if there is a SIM present,
297 // regardless of whether the side stage is enabled. But right now,
298 // the assumption is that narrow screens are phones which have SIMs
299 // and wider screens are tablets which don't. When we do allow this
300 // on devices with a side stage and a SIM, work should be done to
301 // ensure that the main stage is disabled while the dialer is present
302 // in the side stage.
303 showEmergencyCallButton: !shell.sideStageEnabled
305 onEntered: LightDM.Greeter.respond(passphrase);
306 onCancel: greeter.show()
307 onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter fake-active mode
309 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
315 if (lockscreen.delayMinutes > 0) {
316 lockscreen.delayMinutes -= 1
317 if (lockscreen.delayMinutes > 0) {
324 Component.onCompleted: {
325 if (greeter.narrowMode) {
326 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
332 target: LightDM.Greeter
334 onShowGreeter: greeter.show()
335 onHideGreeter: greeter.login()
338 if (greeter.narrowMode) {
339 if (isDefaultPrompt) {
340 if (lockscreen.alphaNumeric) {
341 lockscreen.infoText = i18n.tr("Enter passphrase")
342 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
343 i18n.tr("Please re-enter")
345 lockscreen.infoText = i18n.tr("Enter passcode")
346 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
349 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
350 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
357 onPromptlessChanged: {
358 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
366 onAuthenticationComplete: {
367 if (LightDM.Greeter.authenticated) {
368 AccountsService.failedLogins = 0
370 // Else only penalize user for a failed login if they actually were
371 // prompted for a password. We do this below after the promptless
374 if (LightDM.Greeter.promptless) {
378 if (LightDM.Greeter.authenticated) {
381 AccountsService.failedLogins++
382 if (maxFailedLogins >= 2) { // require at least a warning
383 if (AccountsService.failedLogins === maxFailedLogins - 1) {
384 var title = lockscreen.alphaNumeric ?
385 i18n.tr("Sorry, incorrect passphrase.") :
386 i18n.tr("Sorry, incorrect passcode.")
387 var text = i18n.tr("This will be your last attempt.") + " " +
388 (lockscreen.alphaNumeric ?
389 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
390 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
391 lockscreen.showInfoPopup(title, text)
392 } else if (AccountsService.failedLogins >= maxFailedLogins) {
393 SystemImage.factoryReset() // Ouch!
396 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
397 lockscreen.delayMinutes = failedLoginsDelayMinutes
398 forcedDelayTimer.start()
401 lockscreen.clear(true);
402 if (greeter.narrowMode) {
403 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
410 target: LightDM.Greeter
412 value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
418 opacity: greeterWrapper.showProgress * 0.8
422 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
427 height: parent.height - panel.panelHeight
430 enabled: !launcher.dashSwipe
434 property bool fullyShown: showProgress === 1.0
435 onFullyShownChanged: {
436 // Wait until the greeter is completely covering lockscreen before resetting it.
437 if (fullyShown && !LightDM.Greeter.authenticated) {
443 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
444 onShowProgressChanged: {
445 if (showProgress === 0) {
446 if (LightDM.Greeter.authenticated) {
448 } else if (greeter.narrowMode) {
449 lockscreen.clear(false) // to reset focus if necessary
456 objectName: "greeter"
458 signal sessionStarted() // helpful for tests
460 property string fakeActiveForApp: ""
463 hides: [launcher, panel.indicators]
465 loadContent: required || lockscreen.required // keeps content in memory for quick show()
467 defaultBackground: shell.background
470 height: parent.height
472 dragHandleWidth: shell.edgeSize
476 if (LightDM.Greeter.startSessionSync()) {
487 if (greeter.narrowMode) {
488 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
490 greeter.fakeActiveForApp = "";
491 greeter.forceActiveFocus();
495 /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
496 Component.onCompleted: {
497 Connectivity.unlockAllModems()
500 onUnlocked: greeter.hide()
502 // Update launcher items for new user
503 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
504 AccountsService.user = user;
505 LauncherModel.setUser(user);
508 onTease: launcher.tease()
511 target: ApplicationManager
512 property: "suspended"
513 value: greeter.shown && greeterWrapper.showProgress == 1
523 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
529 function showHome() {
530 if (edgeDemo.running) {
534 if (LightDM.Greeter.active) {
535 if (!LightDM.Greeter.authenticated) {
541 var animate = !LightDM.Greeter.active && !stages.shown
542 dash.setCurrentScope("clickscope", animate, false)
543 ApplicationManager.requestFocusApplication("unity8-dash")
546 function showDash() {
547 if (greeter.fakeActiveForApp !== "") { // just in case user gets here
556 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
557 ApplicationManager.requestFocusApplication("unity8-dash")
571 anchors.fill: parent //because this draws indicator menus
574 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && greeter.fakeActiveForApp === ""
575 contentEnabled: edgeDemo.panelContentEnabled
576 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
578 minimizedPanelHeight: units.gu(3)
579 expandedPanelHeight: units.gu(7)
581 indicatorsModel: visibleIndicators.model
585 id: visibleIndicators
586 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
587 Component.onCompleted: initialise(indicatorProfile)
590 property bool topmostApplicationIsFullscreen:
591 ApplicationManager.focusedApplicationId &&
592 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
594 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
595 || greeter.fakeActiveForApp !== ""
600 objectName: "launcher"
602 readonly property bool dashSwipe: progress > 0
604 anchors.top: parent.top
605 anchors.bottom: parent.bottom
607 dragAreaWidth: shell.edgeSize
608 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && greeter.fakeActiveForApp === ""
610 onShowDashHome: showHome()
612 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
616 onDashSwipeChanged: {
618 dash.setCurrentScope("clickscope", false, true)
621 onLauncherApplicationSelected: {
622 if (greeter.fakeActiveForApp !== "") {
625 if (!edgeDemo.running)
626 shell.activateApplication(appId)
630 panel.indicators.hide()
636 id: modalNotificationBackground
638 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
651 model: NotificationBackend.Model
656 height: parent.height - panel.panelHeight
661 when: overlay.width <= units.gu(60)
662 AnchorChanges { target: notifications; anchors.left: parent.left }
666 when: overlay.width > units.gu(60)
667 AnchorChanges { target: notifications; anchors.left: undefined }
668 PropertyChanges { target: notifications; width: units.gu(38) }
679 shutdownFadeOutRectangle.enabled = true;
680 shutdownFadeOutRectangle.visible = true;
681 shutdownFadeOut.start();
686 id: alphaDisclaimerLabel
687 anchors.centerIn: parent
688 visible: ApplicationManager.fake ? ApplicationManager.fake : false
690 text: "EARLY ALPHA\nNOT READY FOR USE"
693 font.weight: Font.Black
694 horizontalAlignment: Text.AlignHCenter
695 verticalAlignment: Text.AlignVCenter
696 fontSizeMode: Text.Fit
698 scale: Math.min(parent.width, parent.height) / width
703 z: alphaDisclaimerLabel.z + 10
704 paused: Powerd.status === Powerd.Off // Saves power
707 indicators: panel.indicators
712 target: SessionBroadcast
713 onShowHome: showHome()
717 id: shutdownFadeOutRectangle
724 NumberAnimation on opacity {
729 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
730 DBusUnitySessionService.Shutdown();
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list