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 target: ApplicationManager
134 property: "forceDashActive"
135 value: launcher.shown || launcher.dashSwipe
140 // Handle but do not filter out volume keys
141 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
142 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
145 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
146 dialogs.onPowerKeyPressed();
147 event.accepted = true;
149 event.accepted = false;
154 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
155 dialogs.onPowerKeyReleased();
156 event.accepted = true;
158 event.accepted = false;
167 height: parent.height
168 visible: !ApplicationManager.empty
171 target: ApplicationManager
173 if (appId === "dialer-app") {
174 // Always let the dialer-app through. Either user asked
175 // for an emergency call or accepted an incoming call.
176 setFakeActiveForApp(appId)
177 } else if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
183 onFocusedApplicationIdChanged: {
184 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
187 panel.indicators.hide();
190 onApplicationAdded: {
191 if (greeter.shown && appId != "unity8-dash") {
194 if (appId === "dialer-app") {
195 // Always let the dialer-app through. Either user asked
196 // for an emergency call or accepted an incoming call.
197 setFakeActiveForApp(appId)
204 id: applicationsDisplayLoader
207 source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
210 target: applicationsDisplayLoader.item
211 property: "objectName"
215 target: applicationsDisplayLoader.item
216 property: "dragAreaWidth"
217 value: shell.edgeSize
220 target: applicationsDisplayLoader.item
221 property: "maximizedAppTopMargin"
222 // Not just using panel.panelHeight as that changes depending on the focused app.
223 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
226 target: applicationsDisplayLoader.item
227 property: "interactive"
228 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
231 target: applicationsDisplayLoader.item
232 property: "spreadEnabled"
233 value: edgeDemo.stagesEnabled && greeter.fakeActiveForApp === "" // to support emergency dialer hack
236 target: applicationsDisplayLoader.item
237 property: "inverseProgress"
238 value: launcher.progress
241 target: applicationsDisplayLoader.item
242 property: "orientation"
243 value: shell.orientation
250 objectName: "inputMethod"
251 anchors { fill: parent; topMargin: panel.panelHeight }
252 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
256 target: SurfaceManager
258 if (surface.type == MirSurfaceItem.InputMethod) {
259 inputMethod.surface = surface;
263 onSurfaceDestroyed: {
264 if (inputMethod.surface == surface) {
265 inputMethod.surface = null;
266 surface.parent = null;
268 if (!surface.parent) {
269 // there's no one displaying it. delete it right away
275 target: SessionManager
277 if (!session.parentSession && !session.application) {
278 // nothing is using it. delete it right away
286 objectName: "lockscreen"
288 hides: [launcher, panel.indicators]
291 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
292 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
296 height: parent.height - panel.panelHeight
297 background: shell.background
298 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
302 // FIXME: We *should* show emergency dialer if there is a SIM present,
303 // regardless of whether the side stage is enabled. But right now,
304 // the assumption is that narrow screens are phones which have SIMs
305 // and wider screens are tablets which don't. When we do allow this
306 // on devices with a side stage and a SIM, work should be done to
307 // ensure that the main stage is disabled while the dialer is present
308 // in the side stage.
309 showEmergencyCallButton: !shell.sideStageEnabled
311 onEntered: LightDM.Greeter.respond(passphrase);
312 onCancel: greeter.show()
313 onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter fake-active mode
315 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
321 if (lockscreen.delayMinutes > 0) {
322 lockscreen.delayMinutes -= 1
323 if (lockscreen.delayMinutes > 0) {
330 Component.onCompleted: {
331 if (greeter.narrowMode) {
332 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
338 target: LightDM.Greeter
340 onShowGreeter: greeter.show()
341 onHideGreeter: greeter.login()
344 if (greeter.narrowMode) {
345 if (isDefaultPrompt) {
346 if (lockscreen.alphaNumeric) {
347 lockscreen.infoText = i18n.tr("Enter passphrase")
348 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
349 i18n.tr("Please re-enter")
351 lockscreen.infoText = i18n.tr("Enter passcode")
352 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
355 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
356 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
363 onPromptlessChanged: {
364 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
372 onAuthenticationComplete: {
373 if (LightDM.Greeter.authenticated) {
374 AccountsService.failedLogins = 0
376 // Else only penalize user for a failed login if they actually were
377 // prompted for a password. We do this below after the promptless
380 if (LightDM.Greeter.promptless) {
384 if (LightDM.Greeter.authenticated) {
387 AccountsService.failedLogins++
388 if (maxFailedLogins >= 2) { // require at least a warning
389 if (AccountsService.failedLogins === maxFailedLogins - 1) {
390 var title = lockscreen.alphaNumeric ?
391 i18n.tr("Sorry, incorrect passphrase.") :
392 i18n.tr("Sorry, incorrect passcode.")
393 var text = i18n.tr("This will be your last attempt.") + " " +
394 (lockscreen.alphaNumeric ?
395 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
396 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
397 lockscreen.showInfoPopup(title, text)
398 } else if (AccountsService.failedLogins >= maxFailedLogins) {
399 SystemImage.factoryReset() // Ouch!
402 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
403 lockscreen.delayMinutes = failedLoginsDelayMinutes
404 forcedDelayTimer.start()
407 lockscreen.clear(true);
408 if (greeter.narrowMode) {
409 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
416 target: LightDM.Greeter
418 value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
424 opacity: greeterWrapper.showProgress * 0.8
428 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
433 height: parent.height - panel.panelHeight
436 enabled: !launcher.dashSwipe
440 property bool fullyShown: showProgress === 1.0
441 onFullyShownChanged: {
442 // Wait until the greeter is completely covering lockscreen before resetting it.
443 if (fullyShown && !LightDM.Greeter.authenticated) {
449 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
450 onShowProgressChanged: {
451 if (showProgress === 0) {
452 if (LightDM.Greeter.authenticated) {
454 } else if (greeter.narrowMode) {
455 lockscreen.clear(false) // to reset focus if necessary
462 objectName: "greeter"
464 signal sessionStarted() // helpful for tests
466 property string fakeActiveForApp: ""
469 hides: [launcher, panel.indicators]
471 loadContent: required || lockscreen.required // keeps content in memory for quick show()
473 defaultBackground: shell.background
476 height: parent.height
478 dragHandleWidth: shell.edgeSize
482 if (LightDM.Greeter.startSessionSync()) {
493 if (greeter.narrowMode) {
494 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
496 greeter.fakeActiveForApp = "";
497 greeter.forceActiveFocus();
501 /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
502 Component.onCompleted: {
503 Connectivity.unlockAllModems()
506 onUnlocked: greeter.hide()
508 // Update launcher items for new user
509 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
510 AccountsService.user = user;
511 LauncherModel.setUser(user);
514 onTease: launcher.tease()
517 target: ApplicationManager
518 property: "suspended"
519 value: greeter.shown && greeterWrapper.showProgress == 1
529 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
535 function showHome() {
536 if (edgeDemo.running) {
540 if (LightDM.Greeter.active) {
541 if (!LightDM.Greeter.authenticated) {
547 var animate = !LightDM.Greeter.active && !stages.shown
548 dash.setCurrentScope("clickscope", animate, false)
549 ApplicationManager.requestFocusApplication("unity8-dash")
552 function showDash() {
553 if (greeter.fakeActiveForApp !== "") { // just in case user gets here
562 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
563 ApplicationManager.requestFocusApplication("unity8-dash")
577 anchors.fill: parent //because this draws indicator menus
580 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && greeter.fakeActiveForApp === ""
581 contentEnabled: edgeDemo.panelContentEnabled
582 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
584 minimizedPanelHeight: units.gu(3)
585 expandedPanelHeight: units.gu(7)
587 indicatorsModel: visibleIndicators.model
591 id: visibleIndicators
592 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
593 Component.onCompleted: initialise(indicatorProfile)
596 property bool topmostApplicationIsFullscreen:
597 ApplicationManager.focusedApplicationId &&
598 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
600 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
601 || greeter.fakeActiveForApp !== ""
606 objectName: "launcher"
608 readonly property bool dashSwipe: progress > 0
610 anchors.top: parent.top
611 anchors.bottom: parent.bottom
613 dragAreaWidth: shell.edgeSize
614 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && greeter.fakeActiveForApp === ""
616 onShowDashHome: showHome()
618 onDashSwipeChanged: {
620 dash.setCurrentScope("clickscope", false, true)
623 onLauncherApplicationSelected: {
624 if (greeter.fakeActiveForApp !== "") {
627 if (!edgeDemo.running)
628 shell.activateApplication(appId)
632 panel.indicators.hide()
638 id: modalNotificationBackground
640 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
653 model: NotificationBackend.Model
658 height: parent.height - panel.panelHeight
663 when: overlay.width <= units.gu(60)
664 AnchorChanges { target: notifications; anchors.left: parent.left }
668 when: overlay.width > units.gu(60)
669 AnchorChanges { target: notifications; anchors.left: undefined }
670 PropertyChanges { target: notifications; width: units.gu(38) }
681 shutdownFadeOutRectangle.enabled = true;
682 shutdownFadeOutRectangle.visible = true;
683 shutdownFadeOut.start();
688 id: alphaDisclaimerLabel
689 anchors.centerIn: parent
690 visible: ApplicationManager.fake ? ApplicationManager.fake : false
692 text: "EARLY ALPHA\nNOT READY FOR USE"
695 font.weight: Font.Black
696 horizontalAlignment: Text.AlignHCenter
697 verticalAlignment: Text.AlignVCenter
698 fontSizeMode: Text.Fit
700 scale: Math.min(parent.width, parent.height) / width
705 z: alphaDisclaimerLabel.z + 10
706 paused: Powerd.status === Powerd.Off // Saves power
709 indicators: panel.indicators
714 target: SessionBroadcast
715 onShowHome: showHome()
719 id: shutdownFadeOutRectangle
726 NumberAnimation on opacity {
731 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
732 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