Compare commits
204 Commits
wip/waylan
...
wip/smartc
Author | SHA1 | Date | |
---|---|---|---|
d139adc25f | |||
0d151f8d94 | |||
e3fdc2858d | |||
05dcd8575c | |||
214440fde4 | |||
e3621e13ac | |||
df0aace025 | |||
023f4b31d9 | |||
16fba7bd5f | |||
ebce362c05 | |||
c4ca17d127 | |||
3455dd1e76 | |||
57abfab923 | |||
3a13e7484a | |||
a04895383a | |||
43357a1b69 | |||
f38d5a9c06 | |||
696efcdf20 | |||
728c3a72ad | |||
c8c839a569 | |||
6827dacb4b | |||
246271fd9d | |||
4305ebc5b0 | |||
39bff8bfbc | |||
fc6a0c1006 | |||
d6dc9af5ff | |||
e62045eb7f | |||
bda3e53511 | |||
6295d0fc6c | |||
3271e65d56 | |||
f8225141dc | |||
8e9e583b79 | |||
bbadfce65a | |||
9e191d681f | |||
a3236997be | |||
be961cd60e | |||
4efd363134 | |||
7bdee9ce05 | |||
7dc9a9bf2f | |||
87245c7b33 | |||
db497a2ecf | |||
9d250bec05 | |||
1371f69810 | |||
82ee6aed7f | |||
d730fd0a5f | |||
cb4e4bb2db | |||
6fd3c0fbb4 | |||
13170fbb3c | |||
7e7295f259 | |||
d30cb2d4d9 | |||
f4100ac507 | |||
1a0415a100 | |||
1cc5bb5ec4 | |||
43661fff76 | |||
509bdceac2 | |||
835cb8caa5 | |||
ed32adc9bb | |||
44f8fecf84 | |||
0cee0e08e9 | |||
73cd595b73 | |||
c50b23e9ca | |||
d1a763bc21 | |||
de8ca5c1b5 | |||
14372ba7f3 | |||
ca861d8ff9 | |||
37a316e66c | |||
22c8be6645 | |||
8cce1b4f6c | |||
040bb9354a | |||
fb0f9cd1a1 | |||
3816db03f5 | |||
d802416dae | |||
2cd41773ba | |||
8ce5b087bc | |||
03fd74da4e | |||
ed178b702f | |||
b4567acc6b | |||
953f44bcc5 | |||
be4f259b71 | |||
e9531487d9 | |||
d715110961 | |||
3c31908e08 | |||
09d34a2129 | |||
e5e3f3c299 | |||
bcd1c793ea | |||
185152a74a | |||
ded99b9a09 | |||
55a04bbf2b | |||
7d5d7453c2 | |||
952f58153f | |||
d0d981435a | |||
fd83990d8a | |||
ad0c4caf1c | |||
126f0ed95d | |||
a2b499c460 | |||
5a5b3bf291 | |||
a4a6e7cf53 | |||
658db43ad3 | |||
cfecd063c9 | |||
a8fe063726 | |||
dcf637edd8 | |||
c0345f1088 | |||
0cbdfca141 | |||
cb1f696e22 | |||
cf7355e4d0 | |||
dab8c5ea56 | |||
4b889eac32 | |||
86835db8f2 | |||
bc317bf3f2 | |||
5c036eadf9 | |||
263474705b | |||
1e781ec78f | |||
ef1eabf033 | |||
2fa40555e6 | |||
2aae272d86 | |||
7db0900cc8 | |||
c1e2d66abd | |||
78b1ba56ce | |||
5385205b8e | |||
5c8bbb511e | |||
fde01f0b71 | |||
1c04ae3216 | |||
35b4907e52 | |||
2431b8e021 | |||
bd5c04b923 | |||
6e00b6e214 | |||
c9b079cbb5 | |||
9163372786 | |||
dabcd29fb6 | |||
d36e435801 | |||
a18fb27d0f | |||
2dbe511519 | |||
e031a5d28b | |||
f9b32474b0 | |||
53d268a7ef | |||
70da558802 | |||
11215374ff | |||
cb45a38838 | |||
bb4d430ebf | |||
c17f84ca23 | |||
0ae1f9ffc7 | |||
d15bcd9845 | |||
f79a11d993 | |||
9391d9d11b | |||
aee90a3116 | |||
ffac5279a7 | |||
318283fc70 | |||
3582ba0c77 | |||
985d0c786c | |||
9c8c282e08 | |||
93dc7a51c0 | |||
393577ee78 | |||
eef593a34e | |||
3790e924e9 | |||
2f165aade8 | |||
2c502aec45 | |||
6bbf246752 | |||
0b3e8e29cf | |||
d509ab7779 | |||
502a9aefdc | |||
ccba18aa8f | |||
586ebcd5be | |||
317b9a9c87 | |||
67f10ea7eb | |||
793edd3a2b | |||
5dabaf2fe9 | |||
1d9b25f771 | |||
74cd20116a | |||
867695eb4f | |||
9e3592ebf3 | |||
f8ec14d625 | |||
1f21e50663 | |||
7403545a48 | |||
1d95b317ba | |||
f7568b69d4 | |||
8d7649eaec | |||
10b1c7e8a3 | |||
b1c936164c | |||
74ad6abfc2 | |||
9786b2d096 | |||
ea02380c15 | |||
048d5dc914 | |||
aa6b63373e | |||
0b219bf8cb | |||
109b29aeb5 | |||
bc069b99ec | |||
16fa186b63 | |||
e70c0d3e2d | |||
8d47afb195 | |||
a01469fb08 | |||
929636ebd0 | |||
681ef1efec | |||
4f14f122bd | |||
67e9ed5d60 | |||
5c25497e16 | |||
626cbea9cf | |||
aa7ed319e9 | |||
580bd67278 | |||
9e44978aed | |||
64b5ec0b11 | |||
48f9ea3d9e | |||
798a0ca240 | |||
5ee6cbd4c8 | |||
3b219a6a9a |
59
NEWS
59
NEWS
@ -1,3 +1,62 @@
|
||||
3.9.4
|
||||
=====
|
||||
* Fix chat entries not being focused when expanded [Jasper; #698778]
|
||||
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
|
||||
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
|
||||
* Round the ends of slider trough [Jasper; #702825]
|
||||
* Add support for "box-shadow: none" [Cosimo; #702782]
|
||||
* Keep chrome below popup windows [Florian; #702338]
|
||||
* Move the session list to a popup menu [Ray; #702818]
|
||||
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
|
||||
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
|
||||
* Fix management of asynchronous background loading [Lionel; #703001]
|
||||
* Rework focus handling [Jasper; #700735]
|
||||
* Optimize box-shadow rendering [Lionel; #689858]
|
||||
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
|
||||
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
|
||||
#702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
|
||||
#698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
|
||||
|
||||
Contributors:
|
||||
Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
|
||||
Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
|
||||
Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
|
||||
|
||||
Translations:
|
||||
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
|
||||
Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
|
||||
Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
|
||||
|
||||
3.9.3
|
||||
=====
|
||||
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
|
||||
* Tweak timeout for activating windows during XDND [Adel; #700150]
|
||||
* Fix ellipsization in control buttons in app picker [Carlos; #696307]
|
||||
* Fix DND to empty dash [Florian; #684618]
|
||||
* Fix OSD window appearing below system modal dialogs [Rui; #701269]
|
||||
* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
|
||||
* Allow session mode specific overrides schema [Florian; #701717]
|
||||
* window-switcher: Only show windows from current workspace by default
|
||||
[Florian; #701214]
|
||||
* logout dialog: Show the correct text right away [Matthias; #702056]
|
||||
* bluetooth: Port to bluez 5 [Emilio; #700891]
|
||||
* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
|
||||
* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
|
||||
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
|
||||
#700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
|
||||
#701849, #702121]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
|
||||
Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
|
||||
Jonh Wendell
|
||||
|
||||
Translations:
|
||||
Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
|
||||
Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
|
||||
eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
|
||||
Daniel Mustieles [es]
|
||||
|
||||
3.9.2
|
||||
=====
|
||||
* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
|
||||
|
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.9.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.9.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -63,7 +63,7 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
CLUTTER_MIN_VERSION=1.13.4
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.35.4
|
||||
MUTTER_MIN_VERSION=3.9.2
|
||||
MUTTER_MIN_VERSION=3.9.4
|
||||
GTK_MIN_VERSION=3.7.9
|
||||
GIO_MIN_VERSION=2.37.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
|
@ -186,6 +186,19 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.app-switcher"
|
||||
path="/org/gnome/shell/app-switcher/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>false</default>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
If true, only applications that have windows on the current workspace are shown in the switcher.
|
||||
Otherwise, all applications are included.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<enum id="org.gnome.shell.window-switcher.AppIconMode">
|
||||
<value value="1" nick="thumbnail-only"/>
|
||||
<value value="2" nick="app-icon-only"/>
|
||||
|
@ -189,13 +189,6 @@ StScrollBar StButton#vhandle:active {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.popup-combo-menu {
|
||||
background-color: rgba(0,0,0,0.9);
|
||||
padding: 1em 0em;
|
||||
border: 1px solid #5f5f5f;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
/* The remaining popup-menu sizing is all done in ems, so that if you
|
||||
* override .popup-menu.font-size, everything else will scale with it.
|
||||
*/
|
||||
@ -219,10 +212,6 @@ StScrollBar StButton#vhandle:active {
|
||||
.popup-image-menu-item {
|
||||
}
|
||||
|
||||
.popup-combobox-item {
|
||||
spacing: 1em;
|
||||
}
|
||||
|
||||
.popup-separator-menu-item {
|
||||
-gradient-height: 1px;
|
||||
-gradient-start: rgba(255,255,255,0.0);
|
||||
@ -236,10 +225,6 @@ StScrollBar StButton#vhandle:active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.popup-device-menu-item {
|
||||
spacing: .5em;
|
||||
}
|
||||
|
||||
.popup-status-menu-item {
|
||||
font-weight: normal;
|
||||
color: #999;
|
||||
@ -249,19 +234,10 @@ StScrollBar StButton#vhandle:active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.popup-menu-icon {
|
||||
icon-size: 1.09em;
|
||||
}
|
||||
|
||||
.popup-battery-percentage {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
/* Switches */
|
||||
.toggle-switch {
|
||||
width: 65px;
|
||||
@ -286,10 +262,51 @@ StScrollBar StButton#vhandle:active {
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.nm-menu-item-icons {
|
||||
/* Network */
|
||||
|
||||
.nm-dialog {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.nm-dialog-content {
|
||||
spacing: 8px;
|
||||
}
|
||||
|
||||
.nm-dialog-header-hbox {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.nm-dialog-header-icon {
|
||||
icon-size: 32px;
|
||||
}
|
||||
|
||||
.nm-dialog-scroll-view {
|
||||
border: 2px solid #666;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.nm-dialog-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nm-dialog-item {
|
||||
font-size: 12pt;
|
||||
border-bottom: 1px solid #666;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.nm-dialog-item:checked {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.nm-dialog-icons {
|
||||
spacing: .5em;
|
||||
}
|
||||
|
||||
.nm-dialog-icon {
|
||||
icon-size: 16px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.candidate-page-button,
|
||||
@ -327,10 +344,6 @@ StScrollBar StButton#vhandle:active {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.app-view-control:focus {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.app-view-control:first-child:ltr:focus,
|
||||
.app-view-control:last-child:rtl:focus {
|
||||
border-right-width: 1px;
|
||||
@ -365,7 +378,8 @@ StScrollBar StButton#vhandle:active {
|
||||
.modal-dialog-button,
|
||||
.notification-button,
|
||||
.hotplug-notification-item,
|
||||
.app-view-controls {
|
||||
.app-view-controls,
|
||||
#screenShieldNotifications {
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
@ -383,6 +397,7 @@ StScrollBar StButton#vhandle:active {
|
||||
/* Entries */
|
||||
|
||||
#searchEntry,
|
||||
.login-dialog StEntry,
|
||||
.notification StEntry,
|
||||
.modal-dialog StEntry {
|
||||
color: rgb(64, 64, 64);
|
||||
@ -394,6 +409,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
#searchEntry,
|
||||
.login-dialog StEntry,
|
||||
.run-dialog-entry,
|
||||
.notification StEntry {
|
||||
border: 2px solid rgba(245,245,245,0.2);
|
||||
@ -406,6 +422,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
#searchEntry:focus,
|
||||
#searchEntry:hover,
|
||||
.login-dialog StEntry:focus,
|
||||
.notification StEntry:focus,
|
||||
.modal-dialog StEntry {
|
||||
border: 2px solid rgb(136,138,133);
|
||||
@ -415,6 +432,7 @@ StScrollBar StButton#vhandle:active {
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.login-dialog StEntry:focus,
|
||||
.notification StEntry:focus,
|
||||
.modal-dialog StEntry:focus {
|
||||
border: 2px solid #3465a4;
|
||||
@ -438,6 +456,7 @@ StScrollBar StButton#vhandle:active {
|
||||
transition-duration: 0ms;
|
||||
}
|
||||
|
||||
.login-dialog StEntry,
|
||||
.notification StEntry,
|
||||
.modal-dialog StEntry {
|
||||
border-radius: 5px;
|
||||
@ -451,6 +470,7 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.login-dialog StEntry:insensitive,
|
||||
.modal-dialog StEntry:insensitive {
|
||||
border-color: #666666;
|
||||
color: #9f9f9f;
|
||||
@ -468,10 +488,6 @@ StScrollBar StButton#vhandle:active {
|
||||
height: 1.86em;
|
||||
}
|
||||
|
||||
#panel.lock-screen {
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
#panel.unlock-screen,
|
||||
#panel.login-screen {
|
||||
background-color: transparent;
|
||||
@ -606,58 +622,30 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 8px;
|
||||
}
|
||||
|
||||
/* User Menu */
|
||||
|
||||
#panelUserMenu {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.status-chooser {
|
||||
spacing: .4em;
|
||||
}
|
||||
|
||||
.status-chooser .popup-menu-item,
|
||||
.status-chooser-combo .popup-menu-item {
|
||||
padding: .4em;
|
||||
}
|
||||
|
||||
.status-chooser-user-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 5px;
|
||||
width: 48pt;
|
||||
height: 48pt;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.status-chooser-user-icon:hover {
|
||||
border: 2px solid #bbbbbb;
|
||||
}
|
||||
|
||||
.status-chooser-user-name {
|
||||
font-weight: bold;
|
||||
font-size: 1.3em;
|
||||
min-width: 120pt;
|
||||
}
|
||||
|
||||
.status-chooser-combo {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.status-chooser-combo.popup-combo-menu {
|
||||
padding: .4em 0em;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #5f5f5f;
|
||||
}
|
||||
|
||||
.status-chooser-status-item,
|
||||
.status-chooser-combo .popup-combobox-item {
|
||||
spacing: .4em;
|
||||
}
|
||||
|
||||
.system-status-icon {
|
||||
icon-size: 1.09em;
|
||||
}
|
||||
|
||||
.system-switch-user-submenu-icon {
|
||||
icon-size: 24px;
|
||||
border: 1px solid #8b8b8b;
|
||||
}
|
||||
|
||||
.system-menu-action {
|
||||
color: #e6e6e6;
|
||||
border-radius: 4px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.system-menu-action:hover {
|
||||
color: white;
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.system-menu-action > StIcon {
|
||||
icon-size: 32px;
|
||||
}
|
||||
|
||||
/* Overview */
|
||||
|
||||
#overview {
|
||||
@ -905,6 +893,11 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 4px 32px;
|
||||
}
|
||||
|
||||
.app-view-control:focus {
|
||||
padding: 3px 31px;
|
||||
}
|
||||
|
||||
|
||||
.search-display > StBoxLayout,
|
||||
.all-apps > StBoxLayout,
|
||||
.frequent-apps > StBoxLayout {
|
||||
@ -1126,7 +1119,7 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.lg-extension-list {
|
||||
.lg-extensions-list {
|
||||
padding: 4px;
|
||||
spacing: 6px;
|
||||
}
|
||||
@ -1154,11 +1147,6 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Calendar popup */
|
||||
|
||||
#calendarArea {
|
||||
/* this is the total width of the popup */
|
||||
width: 720px;
|
||||
}
|
||||
|
||||
.calendar-vertical-separator {
|
||||
-stipple-width: 1px;
|
||||
-stipple-color: #505050;
|
||||
@ -1296,7 +1284,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.events-table {
|
||||
min-width: 320px;
|
||||
width: 320px;
|
||||
spacing-columns: 6pt;
|
||||
padding: 0 1.4em;
|
||||
}
|
||||
@ -2208,6 +2196,18 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Login Dialog */
|
||||
|
||||
.framed-user-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 5px;
|
||||
width: 48pt;
|
||||
height: 48pt;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.framed-user-icon:hover {
|
||||
border: 2px solid #bbbbbb;
|
||||
}
|
||||
|
||||
.login-dialog-banner {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
@ -2237,6 +2237,10 @@ StScrollBar StButton#vhandle:active {
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.login-dialog-button-box {
|
||||
spacing: 5px;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-login-hint-message {
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
@ -2320,6 +2324,10 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-selection-box .login-dialog-not-listed-label {
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-button:focus .login-dialog-not-listed-label,
|
||||
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
|
||||
color: #E8E8E8;
|
||||
@ -2337,6 +2345,7 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-top: 24px;
|
||||
padding-bottom: 12px;
|
||||
spacing: 8px;
|
||||
width: 23em;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-label {
|
||||
@ -2344,47 +2353,17 @@ StScrollBar StButton#vhandle:active {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry {
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list,
|
||||
.login-dialog-session-list-item {
|
||||
color: #babdb6;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:focus,
|
||||
.login-dialog-session-list-button:active,
|
||||
.login-dialog-session-list-button:hover,
|
||||
.login-dialog-session-list-item:focus,
|
||||
.login-dialog-session-list-item:hover {
|
||||
color: white;
|
||||
.login-dialog-session-list-button StIcon {
|
||||
icon-size: 1.25em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button {
|
||||
padding: 4px;
|
||||
color: #8b8b8b;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-scroll-view {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-triangle {
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-box {
|
||||
padding-left: 6px;
|
||||
spacing: 6px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
.login-dialog-session-list-button:hover,
|
||||
.login-dialog-session-list-button:active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.login-dialog-logo-bin {
|
||||
@ -2451,6 +2430,11 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Screen shield */
|
||||
|
||||
#panel.lock-screen,
|
||||
#screenShieldNotifications {
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.screen-shield-background {
|
||||
background: black;
|
||||
box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
|
||||
@ -2495,33 +2479,27 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
#screenShieldNotifications {
|
||||
border-radius: 8px;
|
||||
background-color: rgba(0.0, 0.0, 0.0, 0.9);
|
||||
border: 2px solid #868686;
|
||||
max-height: 500px;
|
||||
padding: 18px 0;
|
||||
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.screen-shield-notifications-box {
|
||||
spacing: 18px;
|
||||
max-width: 34em;
|
||||
spacing: 12px;
|
||||
width: 30em;
|
||||
}
|
||||
|
||||
.screen-shield-notification-source {
|
||||
padding: 13px 24px;
|
||||
padding: 3px 6px;
|
||||
spacing: 5px;
|
||||
}
|
||||
|
||||
.screen-shield-notification-label {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
padding: 0px 18px;
|
||||
color: #babdb6;
|
||||
padding: 0px 0px 0px 12px;
|
||||
}
|
||||
|
||||
.screen-shield-notification-count-text {
|
||||
padding: 0px 18px;
|
||||
padding: 0px 0px 0px 12px;
|
||||
}
|
||||
|
||||
/* Remove background from notifications, otherwise
|
||||
@ -2539,6 +2517,31 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
#screenShieldNotifications .notification-button,
|
||||
#screenShieldNotifications .notification-icon-button {
|
||||
border: 1px rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
#screenShieldNotifications StScrollBar StBin#trough {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
#screenShieldNotifications StScrollBar StButton#vhandle,
|
||||
#screenShieldNotifications StScrollBar StButton#hhandle {
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
border: none;
|
||||
}
|
||||
|
||||
#screenShieldNotifications StScrollBar StButton#vhandle:hover,
|
||||
#screenShieldNotifications StScrollBar StButton#hhandle {
|
||||
background-color: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
#screenShieldNotifications StScrollBar StButton#vhandle:active,
|
||||
#screenShieldNotifications StScrollBar StButton#hhandle {
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.input-source-switcher-symbol {
|
||||
font-size: 34pt;
|
||||
width: 96px;
|
||||
|
@ -17,6 +17,7 @@ misc/config.js: misc/config.js.in Makefile
|
||||
jsdir = $(pkgdatadir)/js
|
||||
|
||||
nobase_dist_js_DATA = \
|
||||
gdm/authPrompt.js \
|
||||
gdm/batch.js \
|
||||
gdm/fingerprint.js \
|
||||
gdm/loginDialog.js \
|
||||
@ -33,10 +34,13 @@ nobase_dist_js_DATA = \
|
||||
misc/jsParse.js \
|
||||
misc/loginManager.js \
|
||||
misc/modemManager.js \
|
||||
misc/objectManager.js \
|
||||
misc/params.js \
|
||||
misc/smartcardManager.js \
|
||||
misc/util.js \
|
||||
perf/core.js \
|
||||
ui/altTab.js \
|
||||
ui/animation.js \
|
||||
ui/appDisplay.js \
|
||||
ui/appFavorites.js \
|
||||
ui/backgroundMenu.js \
|
||||
@ -94,10 +98,10 @@ nobase_dist_js_DATA = \
|
||||
ui/status/power.js \
|
||||
ui/status/volume.js \
|
||||
ui/status/bluetooth.js \
|
||||
ui/status/system.js \
|
||||
ui/switcherPopup.js \
|
||||
ui/tweener.js \
|
||||
ui/unlockDialog.js \
|
||||
ui/userMenu.js \
|
||||
ui/userWidget.js \
|
||||
ui/viewSelector.js \
|
||||
ui/wanda.js \
|
||||
|
506
js/gdm/authPrompt.js
Normal file
506
js/gdm/authPrompt.js
Normal file
@ -0,0 +1,506 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Animation = imports.ui.animation;
|
||||
const Batch = imports.gdm.batch;
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const Params = imports.misc.params;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
|
||||
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
|
||||
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
|
||||
|
||||
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
|
||||
|
||||
const AuthPromptMode = {
|
||||
UNLOCK_ONLY: 0,
|
||||
UNLOCK_OR_LOG_IN: 1
|
||||
};
|
||||
|
||||
const AuthPromptStatus = {
|
||||
NOT_VERIFYING: 0,
|
||||
VERIFYING: 1,
|
||||
VERIFICATION_FAILED: 2,
|
||||
VERIFICATION_SUCCEEDED: 3
|
||||
};
|
||||
|
||||
const BeginRequestType = {
|
||||
PROVIDE_USERNAME: 0,
|
||||
DONT_PROVIDE_USERNAME: 1
|
||||
};
|
||||
|
||||
const AuthPrompt = new Lang.Class({
|
||||
Name: 'AuthPrompt',
|
||||
|
||||
_init: function(gdmClient, mode) {
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
|
||||
this._gdmClient = gdmClient;
|
||||
this._mode = mode;
|
||||
|
||||
let reauthenticationOnly;
|
||||
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
|
||||
reauthenticationOnly = true;
|
||||
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
|
||||
reauthenticationOnly = false;
|
||||
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
|
||||
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._onShowLoginHint));
|
||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._onHideLoginHint));
|
||||
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
|
||||
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||
|
||||
this.connect('next', Lang.bind(this, function() {
|
||||
this.updateSensitivity(false);
|
||||
this.startSpinning();
|
||||
if (this._queryingService) {
|
||||
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
|
||||
} else {
|
||||
this._preemptiveAnswer = this._entry.text;
|
||||
}
|
||||
}));
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||
vertical: true });
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('key-press-event',
|
||||
Lang.bind(this, function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.cancel();
|
||||
}
|
||||
}));
|
||||
|
||||
this._userWell = new St.Bin({ x_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this.actor.add(this._userWell,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||
|
||||
this.actor.add(this._label,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
ShellEntry.addContextMenu(this._entry, { isPassword: true });
|
||||
|
||||
this.actor.add(this._entry,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START });
|
||||
|
||||
this._entry.grab_key_focus();
|
||||
|
||||
this._message = new St.Label({ opacity: 0 });
|
||||
this._message.clutter_text.line_wrap = true;
|
||||
this.actor.add(this._message, { x_fill: true });
|
||||
|
||||
this._loginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
|
||||
this.actor.add(this._loginHint);
|
||||
|
||||
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
|
||||
vertical: false });
|
||||
this.actor.add(this._buttonBox,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
|
||||
this._defaultButtonWell = new St.Widget();
|
||||
this._defaultButtonWellActor = null;
|
||||
|
||||
this._initButtons();
|
||||
|
||||
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||
this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
|
||||
this._spinner.actor.opacity = 0;
|
||||
this._spinner.actor.show();
|
||||
this._defaultButtonWell.add_child(this._spinner.actor);
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._userVerifier.clear();
|
||||
this._userVerifier.disconnectAll();
|
||||
this._userVerifier = null;
|
||||
},
|
||||
|
||||
_initButtons: function() {
|
||||
this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: _("Cancel") });
|
||||
this.cancelButton.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this.cancel();
|
||||
}));
|
||||
this._buttonBox.add(this.cancelButton,
|
||||
{ expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
|
||||
this._buttonBox.add(this._defaultButtonWell,
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: _("Next") });
|
||||
this.nextButton.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this.emit('next');
|
||||
}));
|
||||
this.nextButton.add_style_pseudo_class('default');
|
||||
this._buttonBox.add(this.nextButton,
|
||||
{ expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.END });
|
||||
|
||||
this._updateNextButtonSensitivity(this._entry.text.length > 0);
|
||||
|
||||
this._entry.clutter_text.connect('text-changed',
|
||||
Lang.bind(this, function() {
|
||||
if (!this._userVerifier.hasPendingMessages)
|
||||
this._fadeOutMessage();
|
||||
|
||||
this._updateNextButtonSensitivity(this._entry.text.length > 0);
|
||||
}));
|
||||
this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
|
||||
this.emit('next');
|
||||
}));
|
||||
},
|
||||
|
||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
if (this._preemptiveAnswer) {
|
||||
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||||
this._preemptiveAnswer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._queryingService)
|
||||
this.clear();
|
||||
|
||||
this._queryingService = serviceName;
|
||||
this.setPasswordChar(passwordChar);
|
||||
this.setQuestion(question);
|
||||
|
||||
if (passwordChar) {
|
||||
if (this._userVerifier.reauthenticating)
|
||||
this.nextButton.label = _("Unlock");
|
||||
else
|
||||
this.nextButton.label = C_("button", "Sign In");
|
||||
} else {
|
||||
this.nextButton.label = _("Next");
|
||||
}
|
||||
|
||||
this.updateSensitivity(true);
|
||||
this.emit('prompted');
|
||||
},
|
||||
|
||||
_onSmartcardStatusChanged: function() {
|
||||
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||
|
||||
// Don't reset on smartcard insertion if we're already verifying
|
||||
// and the smartcard is the main service
|
||||
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
|
||||
this.verificationStatus == AuthPromptStatus.VERIFYING &&
|
||||
this.smartcardDetected)
|
||||
return;
|
||||
|
||||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onShowMessage: function(userVerifier, message, styleClass) {
|
||||
this.setMessage(message, styleClass);
|
||||
this.emit('prompted');
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this.clear();
|
||||
|
||||
this.updateSensitivity(true);
|
||||
this.setActorInDefaultButtonWell(null);
|
||||
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
|
||||
_onShowLoginHint: function(verifier, message) {
|
||||
this.setHint(message);
|
||||
},
|
||||
|
||||
_onHideLoginHint: function() {
|
||||
this.setHint(null);
|
||||
},
|
||||
|
||||
addActorToDefaultButtonWell: function(actor) {
|
||||
this._defaultButtonWell.add_child(actor);
|
||||
|
||||
actor.add_constraint(new Clutter.AlignConstraint({ source: this._spinner.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
},
|
||||
|
||||
setActorInDefaultButtonWell: function(actor, animate) {
|
||||
if (!this._defaultButtonWellActor &&
|
||||
!actor)
|
||||
return;
|
||||
|
||||
let oldActor = this._defaultButtonWellActor;
|
||||
|
||||
if (oldActor)
|
||||
Tweener.removeTweens(oldActor);
|
||||
|
||||
let isSpinner;
|
||||
if (actor == this._spinner.actor)
|
||||
isSpinner = true;
|
||||
else
|
||||
isSpinner = false;
|
||||
|
||||
if (this._defaultButtonWellActor != actor && oldActor) {
|
||||
if (!animate) {
|
||||
oldActor.opacity = 0;
|
||||
} else {
|
||||
Tweener.addTween(oldActor,
|
||||
{ opacity: 0,
|
||||
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||||
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||||
transition: 'linear',
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
if (isSpinner) {
|
||||
if (this._spinner)
|
||||
this._spinner.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (actor) {
|
||||
if (isSpinner)
|
||||
this._spinner.play();
|
||||
|
||||
if (!animate)
|
||||
actor.opacity = 255;
|
||||
else
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 255,
|
||||
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||||
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||||
transition: 'linear' });
|
||||
}
|
||||
|
||||
this._defaultButtonWellActor = actor;
|
||||
},
|
||||
|
||||
startSpinning: function() {
|
||||
this.setActorInDefaultButtonWell(this._spinner.actor, true);
|
||||
},
|
||||
|
||||
stopSpinning: function() {
|
||||
this.setActorInDefaultButtonWell(null, false);
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this._entry.text = '';
|
||||
this.stopSpinning();
|
||||
},
|
||||
|
||||
setPasswordChar: function(passwordChar) {
|
||||
this._entry.clutter_text.set_password_char(passwordChar);
|
||||
this._entry.menu.isPassword = passwordChar != '';
|
||||
},
|
||||
|
||||
setQuestion: function(question) {
|
||||
this._label.set_text(question);
|
||||
|
||||
this._label.show();
|
||||
this._entry.show();
|
||||
|
||||
this._loginHint.opacity = 0;
|
||||
this._loginHint.show();
|
||||
|
||||
this._entry.grab_key_focus();
|
||||
},
|
||||
|
||||
getAnswer: function() {
|
||||
let text = this._entry.get_text();
|
||||
|
||||
return text;
|
||||
},
|
||||
|
||||
_fadeOutMessage: function() {
|
||||
if (this._message.opacity == 0)
|
||||
return;
|
||||
Tweener.removeTweens(this._message);
|
||||
Tweener.addTween(this._message,
|
||||
{ opacity: 0,
|
||||
time: MESSAGE_FADE_OUT_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
setMessage: function(message, styleClass) {
|
||||
if (message) {
|
||||
Tweener.removeTweens(this._message);
|
||||
this._message.text = message;
|
||||
this._message.styleClass = styleClass;
|
||||
this._message.opacity = 255;
|
||||
} else {
|
||||
this._message.opacity = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_updateNextButtonSensitivity: function(sensitive) {
|
||||
this.nextButton.reactive = sensitive;
|
||||
this.nextButton.can_focus = sensitive;
|
||||
},
|
||||
|
||||
updateSensitivity: function(sensitive) {
|
||||
this._updateNextButtonSensitivity(sensitive);
|
||||
this._entry.reactive = sensitive;
|
||||
this._entry.clutter_text.editable = sensitive;
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.setActorInDefaultButtonWell(null, true);
|
||||
this.actor.hide();
|
||||
this._loginHint.opacity = 0;
|
||||
|
||||
this.setUser(null);
|
||||
|
||||
this.updateSensitivity(true);
|
||||
this._entry.set_text('');
|
||||
},
|
||||
|
||||
setUser: function(user) {
|
||||
if (user) {
|
||||
let userWidget = new UserWidget.UserWidget(user);
|
||||
this._userWell.set_child(userWidget.actor);
|
||||
} else {
|
||||
this._userWell.set_child(null);
|
||||
}
|
||||
},
|
||||
|
||||
setHint: function(message) {
|
||||
if (message) {
|
||||
this._loginHint.set_text(message)
|
||||
this._loginHint.opacity = 255;
|
||||
} else {
|
||||
this._loginHint.opacity = 0;
|
||||
this._loginHint.set_text('');
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
let oldStatus = this.verificationStatus;
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
|
||||
if (oldStatus == AuthPromptStatus.VERIFYING)
|
||||
this._userVerifier.cancel();
|
||||
|
||||
this._queryingService = null;
|
||||
this.clear();
|
||||
this._message.opacity = 0;
|
||||
this.setUser(null);
|
||||
this.stopSpinning();
|
||||
this.setHint(null);
|
||||
|
||||
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
|
||||
this.emit('failed');
|
||||
|
||||
let beginRequestType;
|
||||
|
||||
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
|
||||
// The user is constant at the unlock screen
|
||||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||
} else if (this.smartcardDetected &&
|
||||
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
|
||||
// We don't need to know the username if the user preempted the login screen
|
||||
// with a smartcard.
|
||||
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
|
||||
} else {
|
||||
// In all other cases, we should get the username up front.
|
||||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||
}
|
||||
|
||||
this.emit('reset', beginRequestType);
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
if (!this._entry.visible)
|
||||
return;
|
||||
|
||||
this._entry.grab_key_focus();
|
||||
this._entry.clutter_text.insert_unichar(unichar);
|
||||
},
|
||||
|
||||
begin: function(params) {
|
||||
params = Params.parse(params, { userName: null,
|
||||
hold: null });
|
||||
|
||||
this.updateSensitivity(false);
|
||||
|
||||
let hold = params.hold;
|
||||
if (!hold)
|
||||
hold = new Batch.Hold();
|
||||
|
||||
this._userVerifier.begin(params.userName, hold);
|
||||
this.verificationStatus = AuthPromptStatus.VERIFYING;
|
||||
},
|
||||
|
||||
finish: function(onComplete) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
onComplete();
|
||||
}));
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.reset();
|
||||
this.emit('cancelled');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AuthPrompt.prototype);
|
@ -19,28 +19,28 @@
|
||||
*/
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Atk = imports.gi.Atk;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Lang = imports.lang;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Realmd = imports.gdm.realmd;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
|
||||
const AuthPrompt = imports.gdm.authPrompt;
|
||||
const Batch = imports.gdm.batch;
|
||||
const Fprint = imports.gdm.fingerprint;
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Realmd = imports.gdm.realmd;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
@ -287,209 +287,124 @@ const UserList = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(UserList.prototype);
|
||||
|
||||
const SessionListItem = new Lang.Class({
|
||||
Name: 'SessionListItem',
|
||||
|
||||
_init: function(id, name) {
|
||||
this.id = id;
|
||||
|
||||
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
x_align: St.Align.START });
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
|
||||
|
||||
this.actor.add_actor(this._box);
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
|
||||
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
|
||||
this._box.add_actor(this._dot);
|
||||
this.setShowDot(false);
|
||||
|
||||
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
|
||||
text: name });
|
||||
this.actor.label_actor = label;
|
||||
|
||||
this._box.add_actor(label);
|
||||
},
|
||||
|
||||
setShowDot: function(show) {
|
||||
if (show)
|
||||
this._dot.opacity = 255;
|
||||
else
|
||||
this._dot.opacity = 0;
|
||||
},
|
||||
|
||||
_onRepaintDot: function(area) {
|
||||
let cr = area.get_context();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let color = area.get_theme_node().get_foreground_color();
|
||||
|
||||
cr.setSourceRGBA (color.red / 255,
|
||||
color.green / 255,
|
||||
color.blue / 255,
|
||||
color.alpha / 255);
|
||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SessionListItem.prototype);
|
||||
|
||||
const SessionList = new Lang.Class({
|
||||
Name: 'SessionList',
|
||||
const SessionMenuButton = new Lang.Class({
|
||||
Name: 'SessionMenuButton',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Bin();
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list',
|
||||
vertical: true});
|
||||
this.actor.child = this._box;
|
||||
|
||||
let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' });
|
||||
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
reactive: true,
|
||||
track_hover: true,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
let box = new St.BoxLayout();
|
||||
this._button.add_actor(box);
|
||||
accessible_name: _("Choose Session"),
|
||||
accessible_role: Atk.Role.MENU,
|
||||
child: gearIcon });
|
||||
|
||||
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
|
||||
text: '\u25B8' });
|
||||
box.add_actor(this._triangle);
|
||||
this.actor = new St.Bin({ child: this._button });
|
||||
|
||||
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
|
||||
text: _("Session…") });
|
||||
box.add_actor(label);
|
||||
this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP);
|
||||
Main.uiGroup.add_actor(this._menu.actor);
|
||||
this._menu.actor.hide();
|
||||
|
||||
this._button.connect('clicked',
|
||||
Lang.bind(this, this._onClicked));
|
||||
this._box.add_actor(this._button);
|
||||
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||
Gtk.PolicyType.AUTOMATIC);
|
||||
this._box.add_actor(this._scrollView);
|
||||
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
|
||||
vertical: true });
|
||||
this._scrollView.add_actor(this._itemList);
|
||||
this._scrollView.hide();
|
||||
this.isOpen = false;
|
||||
this._menu.connect('open-state-changed',
|
||||
Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen)
|
||||
this._button.add_style_pseudo_class('active');
|
||||
else
|
||||
this._button.remove_style_pseudo_class('active');
|
||||
}));
|
||||
|
||||
let subtitle = new PopupMenu.PopupMenuItem(_("Session"), { style_class: 'popup-subtitle-menu-item',
|
||||
reactive: false });
|
||||
this._menu.addMenuItem(subtitle);
|
||||
|
||||
this._manager = new PopupMenu.PopupMenuManager({ actor: this._button });
|
||||
this._manager.addMenu(this._menu);
|
||||
|
||||
this._button.connect('clicked', Lang.bind(this, function() {
|
||||
this._menu.toggle();
|
||||
}));
|
||||
|
||||
this._items = {};
|
||||
this._activeSessionId = null;
|
||||
this._populate();
|
||||
},
|
||||
|
||||
open: function() {
|
||||
if (this.isOpen)
|
||||
return;
|
||||
|
||||
this._button.add_style_pseudo_class('open');
|
||||
this._scrollView.show();
|
||||
this._triangle.set_text('\u25BE');
|
||||
|
||||
this.isOpen = true;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (!this.isOpen)
|
||||
return;
|
||||
|
||||
this._button.remove_style_pseudo_class('open');
|
||||
this._scrollView.hide();
|
||||
this._triangle.set_text('\u25B8');
|
||||
|
||||
this.isOpen = false;
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
if (!this.isOpen)
|
||||
this.open();
|
||||
else
|
||||
this.close();
|
||||
},
|
||||
|
||||
updateSensitivity: function(sensitive) {
|
||||
this._button.reactive = sensitive;
|
||||
this._button.can_focus = sensitive;
|
||||
this._menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
},
|
||||
|
||||
for (let id in this._items)
|
||||
this._items[id].actor.reactive = sensitive;
|
||||
_updateOrnament: function() {
|
||||
let itemIds = Object.keys(this._items);
|
||||
for (let i = 0; i < itemIds.length; i++) {
|
||||
if (itemIds[i] == this._activeSessionId)
|
||||
this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.DOT);
|
||||
else
|
||||
this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.NONE);
|
||||
}
|
||||
},
|
||||
|
||||
setActiveSession: function(sessionId) {
|
||||
if (sessionId == this._activeSessionId)
|
||||
return;
|
||||
|
||||
if (this._activeSessionId)
|
||||
this._items[this._activeSessionId].setShowDot(false);
|
||||
|
||||
this._items[sessionId].setShowDot(true);
|
||||
this._activeSessionId = sessionId;
|
||||
this._updateOrnament();
|
||||
|
||||
this.emit('session-activated', this._activeSessionId);
|
||||
},
|
||||
|
||||
_populate: function() {
|
||||
this._itemList.destroy_all_children();
|
||||
this._activeSessionId = null;
|
||||
this._items = {};
|
||||
close: function() {
|
||||
this._menu.close();
|
||||
},
|
||||
|
||||
_populate: function() {
|
||||
let ids = Gdm.get_session_ids();
|
||||
ids.sort();
|
||||
|
||||
if (ids.length <= 1) {
|
||||
this._box.hide();
|
||||
this._button.hide();
|
||||
} else {
|
||||
this._button.show();
|
||||
this._box.show();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]);
|
||||
|
||||
let item = new SessionListItem(ids[i], sessionName);
|
||||
this._itemList.add_actor(item.actor);
|
||||
this._items[ids[i]] = item;
|
||||
let id = ids[i];
|
||||
let item = new PopupMenu.PopupMenuItem(sessionName);
|
||||
this._menu.addMenuItem(item);
|
||||
this._items[id] = item;
|
||||
|
||||
if (!this._activeSessionId)
|
||||
this.setActiveSession(ids[i]);
|
||||
this.setActiveSession(id);
|
||||
|
||||
item.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
this.setActiveSession(item.id);
|
||||
}));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this.setActiveSession(id);
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SessionList.prototype);
|
||||
Signals.addSignalMethods(SessionMenuButton.prototype);
|
||||
|
||||
const LoginDialog = new Lang.Class({
|
||||
Name: 'LoginDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(parentActor) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
parentActor: parentActor,
|
||||
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
|
||||
shouldFadeIn: false });
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
this.connect('opened',
|
||||
Lang.bind(this, this._onOpened));
|
||||
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
|
||||
style_class: 'login-dialog',
|
||||
visible: false });
|
||||
|
||||
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
parentActor.add_child(this.actor);
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default()
|
||||
this._greeterClient = new Gdm.Client();
|
||||
let gdmClient = new Gdm.Client();
|
||||
|
||||
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
|
||||
this._greeter = this._greeterClient.get_greeter_sync(null);
|
||||
this._greeter = gdmClient.get_greeter_sync(null);
|
||||
|
||||
this._greeter.connect('default-session-name-changed',
|
||||
Lang.bind(this, this._onDefaultSessionChanged));
|
||||
@ -500,15 +415,6 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, this._onTimedLoginRequested));
|
||||
}
|
||||
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
|
||||
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._verificationFailed));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
|
||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
|
||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
|
||||
this._verifyingUser = false;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
|
||||
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
|
||||
@ -525,8 +431,12 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, this._updateLogoTexture));
|
||||
|
||||
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
|
||||
vertical: true });
|
||||
this.contentLayout.add(this._userSelectionBox);
|
||||
vertical: true,
|
||||
visible: false });
|
||||
this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
this.actor.add_child(this._userSelectionBox);
|
||||
|
||||
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
||||
text: '' });
|
||||
@ -539,60 +449,18 @@ const LoginDialog = new Lang.Class({
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
this.setInitialKeyFocus(this._userList.actor);
|
||||
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
|
||||
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
|
||||
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._authPrompt.hide();
|
||||
|
||||
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||
vertical: true });
|
||||
this.contentLayout.add(this._promptBox,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._promptUser = new St.Bin({ x_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._promptBox.add(this._promptUser,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||
this._authPrompt.actor.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
|
||||
this._promptBox.add(this._promptLabel,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
this._promptEntryTextChangedId = 0;
|
||||
this._promptEntryActivateId = 0;
|
||||
this._promptBox.add(this._promptEntry,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START });
|
||||
|
||||
this._promptMessage = new St.Label({ visible: false });
|
||||
this._promptBox.add(this._promptMessage, { x_fill: true });
|
||||
|
||||
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
|
||||
this._promptLoginHint.hide();
|
||||
this._promptBox.add(this._promptLoginHint);
|
||||
|
||||
this._signInButton = null;
|
||||
|
||||
this._sessionList = new SessionList();
|
||||
this._sessionList.connect('session-activated',
|
||||
Lang.bind(this, function(list, sessionId) {
|
||||
this._greeter.call_select_session_sync (sessionId, null);
|
||||
}));
|
||||
|
||||
this._promptBox.add(this._sessionList.actor,
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._promptBox.hide();
|
||||
this.actor.add_child(this._authPrompt.actor);
|
||||
this._userList.actor.add_constraint(new Clutter.BindConstraint({ source: this._authPrompt.actor,
|
||||
coordinate: Clutter.BindCoordinate.WIDTH }));
|
||||
|
||||
// translators: this message is shown below the user list on the
|
||||
// login screen. It can be activated to reveal an entry for
|
||||
@ -607,7 +475,13 @@ const LoginDialog = new Lang.Class({
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
|
||||
this._notListedButton.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this._authPrompt.cancelButton.show();
|
||||
this._hideUserListAskForUsernameAndBeginVerification();
|
||||
}));
|
||||
|
||||
this._notListedButton.hide();
|
||||
|
||||
this._userSelectionBox.add(this._notListedButton,
|
||||
{ expand: false,
|
||||
@ -616,7 +490,13 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
this._logoBin = new St.Bin({ style_class: 'login-dialog-logo-bin', y_expand: true });
|
||||
this._logoBin.set_y_align(Clutter.ActorAlign.END);
|
||||
this.backgroundStack.add_actor(this._logoBin);
|
||||
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.X_AXIS,
|
||||
factor: 0.5 }));
|
||||
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.Y_AXIS,
|
||||
factor: 1.0 }));
|
||||
this.actor.add_child(this._logoBin);
|
||||
this._updateLogo();
|
||||
|
||||
if (!this._userManager.is_loaded)
|
||||
@ -636,20 +516,26 @@ const LoginDialog = new Lang.Class({
|
||||
this._onUserListActivated(item);
|
||||
}));
|
||||
|
||||
|
||||
this._sessionMenuButton = new SessionMenuButton();
|
||||
this._sessionMenuButton.connect('session-activated',
|
||||
Lang.bind(this, function(list, sessionId) {
|
||||
this._greeter.call_select_session_sync (sessionId, null);
|
||||
}));
|
||||
this._sessionMenuButton.actor.opacity = 0;
|
||||
this._sessionMenuButton.actor.show();
|
||||
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
|
||||
|
||||
},
|
||||
|
||||
_updateDisableUserList: function() {
|
||||
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
|
||||
|
||||
// If this is the first time around, set initial focus
|
||||
if (this._disableUserList == undefined && disableUserList)
|
||||
this.setInitialKeyFocus(this._promptEntry);
|
||||
|
||||
if (disableUserList != this._disableUserList) {
|
||||
this._disableUserList = disableUserList;
|
||||
|
||||
if (!this._verifyingUser)
|
||||
this._reset();
|
||||
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
|
||||
this._authPrompt.reset();
|
||||
}
|
||||
},
|
||||
|
||||
@ -683,195 +569,58 @@ const LoginDialog = new Lang.Class({
|
||||
this._updateLogoTexture(this._textureCache, this._logoFileUri);
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._userVerifier.clear();
|
||||
_onPrompted: function() {
|
||||
this._sessionMenuButton.updateSensitivity(true);
|
||||
|
||||
this._updateSensitivity(true);
|
||||
this._promptMessage.hide();
|
||||
this._user = null;
|
||||
this._verifyingUser = false;
|
||||
|
||||
if (this._disableUserList)
|
||||
this._hideUserListAndLogIn();
|
||||
else
|
||||
this._showUserList();
|
||||
if (this._shouldShowSessionMenuButton())
|
||||
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
|
||||
this._showPrompt();
|
||||
},
|
||||
|
||||
_verificationFailed: function() {
|
||||
this._promptEntry.text = '';
|
||||
_onReset: function(authPrompt, beginRequest) {
|
||||
this._sessionMenuButton.updateSensitivity(true);
|
||||
|
||||
this._updateSensitivity(true);
|
||||
this.setWorking(false);
|
||||
this._user = null;
|
||||
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
if (this._disableUserList) {
|
||||
this._authPrompt.cancelButton.hide();
|
||||
this._hideUserListAskForUsernameAndBeginVerification();
|
||||
} else {
|
||||
this._showUserList();
|
||||
}
|
||||
} else {
|
||||
this._authPrompt.cancelButton.hide();
|
||||
this._hideUserListAndBeginVerification();
|
||||
}
|
||||
},
|
||||
|
||||
_onDefaultSessionChanged: function(client, sessionId) {
|
||||
this._sessionList.setActiveSession(sessionId);
|
||||
this._sessionMenuButton.setActiveSession(sessionId);
|
||||
},
|
||||
|
||||
_showMessage: function(userVerifier, message, styleClass) {
|
||||
if (message) {
|
||||
this._promptMessage.text = message;
|
||||
this._promptMessage.styleClass = styleClass;
|
||||
this._promptMessage.show();
|
||||
} else {
|
||||
this._promptMessage.hide();
|
||||
}
|
||||
_shouldShowSessionMenuButton: function() {
|
||||
if (this._authPrompt.verifyingUser)
|
||||
return true;
|
||||
|
||||
if (!this._user)
|
||||
return false;
|
||||
|
||||
if (this._user.is_logged_in)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_showLoginHint: function(verifier, message) {
|
||||
this._promptLoginHint.set_text(message)
|
||||
this._promptLoginHint.show();
|
||||
this._promptLoginHint.opacity = 255;
|
||||
},
|
||||
|
||||
_hideLoginHint: function() {
|
||||
this._promptLoginHint.hide();
|
||||
this._promptLoginHint.set_text('');
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (this._verifyingUser)
|
||||
this._userVerifier.cancel();
|
||||
else
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_showPrompt: function(forSecret) {
|
||||
this._sessionList.actor.hide();
|
||||
this._promptLabel.show();
|
||||
this._promptEntry.show();
|
||||
this._promptLoginHint.opacity = 0;
|
||||
this._promptLoginHint.show();
|
||||
this._promptBox.opacity = 0;
|
||||
this._promptBox.show();
|
||||
Tweener.addTween(this._promptBox,
|
||||
_showPrompt: function() {
|
||||
if (this._authPrompt.actor.visible)
|
||||
return;
|
||||
this._authPrompt.actor.opacity = 0;
|
||||
this._authPrompt.actor.show();
|
||||
Tweener.addTween(this._authPrompt.actor,
|
||||
{ opacity: 255,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
|
||||
if ((this._user && !this._user.is_logged_in()) || this._verifyingUser)
|
||||
this._sessionList.actor.show();
|
||||
|
||||
this._promptEntry.grab_key_focus();
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
let tasks = [function() {
|
||||
this._prepareDialog(forSecret, hold);
|
||||
},
|
||||
|
||||
hold];
|
||||
|
||||
let batch = new Batch.ConcurrentBatch(this, tasks);
|
||||
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_prepareDialog: function(forSecret, hold) {
|
||||
this.buttonLayout.visible = true;
|
||||
this.clearButtons();
|
||||
|
||||
if (!this._disableUserList || this._verifyingUser)
|
||||
this.addButton({ action: Lang.bind(this, this.cancel),
|
||||
label: _("Cancel"),
|
||||
key: Clutter.Escape },
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this.placeSpinner({ expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this._signInButton = this.addButton({ action: Lang.bind(this, function() {
|
||||
hold.release();
|
||||
}),
|
||||
label: forSecret ? C_("button", "Sign In") : _("Next"),
|
||||
default: true },
|
||||
{ expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
|
||||
|
||||
this._promptEntryTextChangedId =
|
||||
this._promptEntry.clutter_text.connect('text-changed',
|
||||
Lang.bind(this, function() {
|
||||
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
|
||||
}));
|
||||
|
||||
this._promptEntryActivateId =
|
||||
this._promptEntry.clutter_text.connect('activate', function() {
|
||||
hold.release();
|
||||
});
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._promptEntry.reactive = sensitive;
|
||||
this._promptEntry.clutter_text.editable = sensitive;
|
||||
this._sessionList.updateSensitivity(sensitive);
|
||||
this._updateSignInButtonSensitivity(sensitive);
|
||||
},
|
||||
|
||||
_updateSignInButtonSensitivity: function(sensitive) {
|
||||
if (this._signInButton) {
|
||||
this._signInButton.reactive = sensitive;
|
||||
this._signInButton.can_focus = sensitive;
|
||||
}
|
||||
},
|
||||
|
||||
_hidePrompt: function() {
|
||||
this.setButtons([]);
|
||||
|
||||
if (this._promptEntryTextChangedId > 0) {
|
||||
this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId);
|
||||
this._promptEntryTextChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._promptEntryActivateId > 0) {
|
||||
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
|
||||
this._promptEntryActivateId = 0;
|
||||
}
|
||||
|
||||
this.setWorking(false);
|
||||
this._promptBox.hide();
|
||||
this._promptLoginHint.hide();
|
||||
|
||||
this._promptUser.set_child(null);
|
||||
|
||||
this._updateSensitivity(true);
|
||||
this._promptEntry.set_text('');
|
||||
|
||||
this._sessionList.close();
|
||||
this._promptLoginHint.hide();
|
||||
|
||||
this.clearButtons();
|
||||
this._signInButton = null;
|
||||
},
|
||||
|
||||
_askQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
this._promptLabel.set_text(question);
|
||||
|
||||
this._updateSensitivity(true);
|
||||
this._promptEntry.set_text('');
|
||||
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
||||
|
||||
let tasks = [function() {
|
||||
return this._showPrompt(!!passwordChar);
|
||||
},
|
||||
|
||||
function() {
|
||||
let text = this._promptEntry.get_text();
|
||||
this._updateSensitivity(false);
|
||||
this.setWorking(true);
|
||||
this._userVerifier.answerQuery(serviceName, text);
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_showRealmLoginHint: function(realmManager, hint) {
|
||||
@ -884,38 +633,36 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
// Translators: this message is shown below the username entry field
|
||||
// to clue the user in on how to login to the local network realm
|
||||
this._showLoginHint(null, _("(e.g., user or %s)").format(hint));
|
||||
this._authPrompt.setHint(_("(e.g., user or %s)").format(hint));
|
||||
},
|
||||
|
||||
_askForUsernameAndLogIn: function() {
|
||||
this._promptLabel.set_text(_("Username: "));
|
||||
this._promptEntry.set_text('');
|
||||
this._promptEntry.clutter_text.set_password_char('');
|
||||
_askForUsernameAndBeginVerification: function() {
|
||||
this._authPrompt.setPasswordChar('');
|
||||
this._authPrompt.setQuestion(_("Username: "));
|
||||
|
||||
let realmManager = new Realmd.Manager();
|
||||
let signalId = realmManager.connect('login-format-changed',
|
||||
Lang.bind(this, this._showRealmLoginHint));
|
||||
let realmSignalId = realmManager.connect('login-format-changed',
|
||||
Lang.bind(this, this._showRealmLoginHint));
|
||||
this._showRealmLoginHint(realmManager.loginFormat);
|
||||
|
||||
let tasks = [this._showPrompt,
|
||||
let nextSignalId = this._authPrompt.connect('next',
|
||||
Lang.bind(this, function() {
|
||||
this._authPrompt.disconnect(nextSignalId);
|
||||
this._authPrompt.updateSensitivity(false);
|
||||
let answer = this._authPrompt.getAnswer();
|
||||
this._authPrompt.clear();
|
||||
this._authPrompt.cancelButton.show();
|
||||
this._authPrompt.startSpinning();
|
||||
this._authPrompt.begin({ userName: answer });
|
||||
|
||||
function() {
|
||||
let userName = this._promptEntry.get_text();
|
||||
this._promptEntry.reactive = false;
|
||||
return this._beginVerificationForUser(userName);
|
||||
},
|
||||
|
||||
function() {
|
||||
realmManager.disconnect(signalId)
|
||||
realmManager.release();
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
return batch.run();
|
||||
realmManager.disconnect(realmSignalId)
|
||||
realmManager.release();
|
||||
}));
|
||||
this._showPrompt();
|
||||
},
|
||||
|
||||
_startSession: function(serviceName) {
|
||||
Tweener.addTween(this.dialogLayout,
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
@ -924,7 +671,7 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i] != Main.layoutManager.screenShieldGroup)
|
||||
children[i].opacity = this.dialogLayout.opacity;
|
||||
children[i].opacity = this.actor.opacity;
|
||||
}
|
||||
},
|
||||
onUpdateScope: this,
|
||||
@ -938,15 +685,9 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onSessionOpened: function(client, serviceName) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._authPrompt.finish(Lang.bind(this, function() {
|
||||
this._startSession(serviceName);
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._startSession(serviceName);
|
||||
}));
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_waitForItemForUser: function(userName) {
|
||||
@ -1078,38 +819,42 @@ const LoginDialog = new Lang.Class({
|
||||
this._userSelectionBox.visible = expanded;
|
||||
},
|
||||
|
||||
_hideUserListAndLogIn: function() {
|
||||
_hideUserList: function() {
|
||||
this._setUserListExpanded(false);
|
||||
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||
this._askForUsernameAndLogIn();
|
||||
if (this._userSelectionBox.visible)
|
||||
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||
},
|
||||
|
||||
_hideUserListAskForUsernameAndBeginVerification: function() {
|
||||
this._hideUserList();
|
||||
this._askForUsernameAndBeginVerification();
|
||||
},
|
||||
|
||||
_hideUserListAndBeginVerification: function() {
|
||||
this._hideUserList();
|
||||
this._authPrompt.begin();
|
||||
},
|
||||
|
||||
_showUserList: function() {
|
||||
this._hidePrompt();
|
||||
this._authPrompt.hide();
|
||||
this._sessionMenuButton.close();
|
||||
this._authPrompt.cancelButton.show();
|
||||
this._setUserListExpanded(true);
|
||||
this._notListedButton.show();
|
||||
this._userList.actor.grab_key_focus();
|
||||
},
|
||||
|
||||
_beginVerificationForUser: function(userName) {
|
||||
_beginVerificationForItem: function(item) {
|
||||
this._authPrompt.setUser(item.user);
|
||||
|
||||
let userName = item.user.get_user_name();
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
this._userVerifier.begin(userName, hold);
|
||||
this._verifyingUser = true;
|
||||
this._authPrompt.begin({ userName: userName,
|
||||
hold: hold });
|
||||
return hold;
|
||||
},
|
||||
|
||||
_beginVerificationForItem: function(item) {
|
||||
let userWidget = new UserWidget.UserWidget(item.user);
|
||||
this._promptUser.set_child(userWidget.actor);
|
||||
|
||||
let tasks = [function() {
|
||||
let userName = item.user.get_user_name();
|
||||
return this._beginVerificationForUser(userName);
|
||||
}];
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_onUserListActivated: function(activatedItem) {
|
||||
let tasks = [function() {
|
||||
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||
@ -1152,21 +897,23 @@ const LoginDialog = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_onOpened: function() {
|
||||
Main.ctrlAltTabManager.addGroup(this.dialogLayout,
|
||||
open: function() {
|
||||
Main.ctrlAltTabManager.addGroup(this.actor,
|
||||
_("Login Window"),
|
||||
'dialog-password-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
|
||||
this._userList.actor.grab_key_focus();
|
||||
this.actor.show();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.parent();
|
||||
|
||||
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
||||
this._authPrompt.addCharacter(unichar);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(LoginDialog.prototype);
|
||||
|
149
js/gdm/util.js
149
js/gdm/util.js
@ -6,20 +6,26 @@ const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
const Fprint = imports.gdm.fingerprint;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const SmartcardManager = imports.misc.smartcardManager;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
|
||||
const FADE_ANIMATION_TIME = 0.16;
|
||||
const CLONE_FADE_ANIMATION_TIME = 0.25;
|
||||
|
||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
|
||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
|
||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||
@ -114,11 +120,34 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._client = client;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||
this._settings.connect('changed',
|
||||
Lang.bind(this, function() {
|
||||
this._updateDefaultService();
|
||||
}));
|
||||
this._updateDefaultService();
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||
|
||||
// We check for smartcards right away, since an inserted smartcard
|
||||
// at startup should result in immediately initiating authentication.
|
||||
// This is different than fingeprint readers, where we only check them
|
||||
// after a user has been picked.
|
||||
this._checkForSmartcard();
|
||||
|
||||
this._smartcardManager.connect('smartcard-inserted',
|
||||
Lang.bind(this, function() {
|
||||
this._checkForSmartcard();
|
||||
}));
|
||||
this._smartcardManager.connect('smartcard-removed',
|
||||
Lang.bind(this, function() {
|
||||
this._checkForSmartcard();
|
||||
}));
|
||||
|
||||
this._messageQueue = [];
|
||||
this._messageQueueTimeoutId = 0;
|
||||
this.hasPendingMessages = false;
|
||||
this.reauthenticating = false;
|
||||
|
||||
this._failCounter = 0;
|
||||
},
|
||||
@ -127,6 +156,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
this._hold = hold;
|
||||
this._userName = userName;
|
||||
this.reauthenticating = false;
|
||||
|
||||
this._checkForFingerprintReader();
|
||||
|
||||
@ -144,8 +174,10 @@ const ShellUserVerifier = new Lang.Class({
|
||||
if (this._cancellable)
|
||||
this._cancellable.cancel();
|
||||
|
||||
if (this._userVerifier)
|
||||
if (this._userVerifier) {
|
||||
this._userVerifier.call_cancel_sync(null);
|
||||
this.clear();
|
||||
}
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
@ -163,14 +195,14 @@ const ShellUserVerifier = new Lang.Class({
|
||||
},
|
||||
|
||||
answerQuery: function(serviceName, answer) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
if (!this.hasPendingMessages) {
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
}));
|
||||
let signalId = this.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this.disconnect(signalId);
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
@ -240,6 +272,28 @@ const ShellUserVerifier = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_checkForSmartcard: function() {
|
||||
let smartcardDetected;
|
||||
|
||||
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||
smartcardDetected = false;
|
||||
else if (this.reauthenticating)
|
||||
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
|
||||
else
|
||||
smartcardDetected = this._smartcardManager.hasInsertedTokens();
|
||||
|
||||
if (smartcardDetected != this.smartcardDetected) {
|
||||
this.smartcardDetected = smartcardDetected;
|
||||
|
||||
if (this.smartcardDetected)
|
||||
this._preemptingService = SMARTCARD_SERVICE_NAME;
|
||||
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
|
||||
this._preemptingService = null;
|
||||
|
||||
this.emit('smartcard-status-changed');
|
||||
}
|
||||
},
|
||||
|
||||
_reportInitError: function(where, error) {
|
||||
logError(error, where);
|
||||
this._hold.release();
|
||||
@ -265,6 +319,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
this.reauthenticating = true;
|
||||
this._connectSignals();
|
||||
this._beginVerification();
|
||||
this._hold.release();
|
||||
@ -295,11 +350,35 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
},
|
||||
|
||||
_getForegroundService: function() {
|
||||
if (this._preemptingService)
|
||||
return this._preemptingService;
|
||||
|
||||
return this._defaultService;
|
||||
},
|
||||
|
||||
serviceIsForeground: function(serviceName) {
|
||||
return serviceName == this._getForegroundService();
|
||||
},
|
||||
|
||||
serviceIsDefault: function(serviceName) {
|
||||
return serviceName == this._defaultService;
|
||||
},
|
||||
|
||||
_updateDefaultService: function() {
|
||||
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
|
||||
this._defaultService = PASSWORD_SERVICE_NAME;
|
||||
else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||
this._defaultService = SMARTCARD_SERVICE_NAME;
|
||||
else if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
|
||||
this._defaultService = FINGERPRINT_SERVICE_NAME;
|
||||
},
|
||||
|
||||
_beginVerification: function() {
|
||||
this._hold.acquire();
|
||||
|
||||
if (this._userName) {
|
||||
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
|
||||
this._userVerifier.call_begin_verification_for_user(this._getForegroundService(),
|
||||
this._userName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
@ -315,7 +394,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._hold.release();
|
||||
}));
|
||||
|
||||
if (this._haveFingerprintReader) {
|
||||
if (this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME)) {
|
||||
this._hold.acquire();
|
||||
|
||||
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
|
||||
@ -335,7 +414,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
|
||||
this._userVerifier.call_begin_verification(this._getForegroundService(),
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
@ -353,39 +432,36 @@ const ShellUserVerifier = new Lang.Class({
|
||||
},
|
||||
|
||||
_onInfo: function(client, serviceName, info) {
|
||||
// We don't display fingerprint messages, because they
|
||||
// have words like UPEK in them. Instead we use the messages
|
||||
// as a cue to display our own message.
|
||||
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||
if (this.serviceIsForeground(serviceName)) {
|
||||
this._queueMessage(info, 'login-dialog-message-info');
|
||||
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||
this._haveFingerprintReader) {
|
||||
// We don't show fingeprint messages directly since it's
|
||||
// not the main auth service. Instead we use the messages
|
||||
// as a cue to display our own message.
|
||||
|
||||
// Translators: this message is shown below the password entry field
|
||||
// to indicate the user can swipe their finger instead
|
||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this._queueMessage(info, 'login-dialog-message-info');
|
||||
}
|
||||
},
|
||||
|
||||
_onProblem: function(client, serviceName, problem) {
|
||||
// we don't want to show auth failed messages to
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
if (!this.serviceIsForeground(serviceName))
|
||||
return;
|
||||
|
||||
this._queueMessage(problem, 'login-dialog-message-warning');
|
||||
},
|
||||
|
||||
_onInfoQuery: function(client, serviceName, question) {
|
||||
// We only expect questions to come from the main auth service
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
if (!this.serviceIsForeground(serviceName))
|
||||
return;
|
||||
|
||||
this.emit('ask-question', serviceName, question, '');
|
||||
},
|
||||
|
||||
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||
// We only expect secret requests to come from the main auth service
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
if (!this.serviceIsForeground(serviceName))
|
||||
return;
|
||||
|
||||
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
||||
@ -394,6 +470,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
_onReset: function() {
|
||||
// Clear previous attempts to authenticate
|
||||
this._failCounter = 0;
|
||||
this._updateDefaultService();
|
||||
|
||||
this.emit('reset');
|
||||
},
|
||||
@ -422,24 +499,24 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||
|
||||
if (canRetry) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
if (!this.hasPendingMessages) {
|
||||
this._retry();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._retry();
|
||||
}));
|
||||
let signalId = this.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this.disconnect(signalId);
|
||||
this._retry();
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
if (!this.hasPendingMessages) {
|
||||
this._cancelAndReset();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._cancelAndReset();
|
||||
}));
|
||||
let signalId = this.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this.disconnect(signalId);
|
||||
this._cancelAndReset();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,7 +527,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// if the password service fails, then cancel everything.
|
||||
// But if, e.g., fingerprint fails, still give
|
||||
// password authentication a chance to succeed
|
||||
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
if (this.serviceIsForeground(serviceName)) {
|
||||
this._verificationFailed(true);
|
||||
}
|
||||
|
||||
|
275
js/misc/objectManager.js
Normal file
275
js/misc/objectManager.js
Normal file
@ -0,0 +1,275 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
const Signals = imports.signals;
|
||||
|
||||
// Specified in the D-Bus specification here:
|
||||
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
|
||||
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager">
|
||||
<method name="GetManagedObjects">
|
||||
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
|
||||
</method>
|
||||
<signal name="InterfacesAdded">
|
||||
<arg name="objectPath" type="o"/>
|
||||
<arg name="interfaces" type="a{sa{sv}}" />
|
||||
</signal>
|
||||
<signal name="InterfacesRemoved">
|
||||
<arg name="objectPath" type="o"/>
|
||||
<arg name="interfaces" type="as" />
|
||||
</signal>
|
||||
</interface>
|
||||
|
||||
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
|
||||
|
||||
const ObjectManager = new Lang.Class({
|
||||
Name: 'ObjectManager',
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { connection: null,
|
||||
name: null,
|
||||
objectPath: null,
|
||||
knownInterfaces: null,
|
||||
cancellable: null,
|
||||
onLoaded: null });
|
||||
|
||||
this._connection = params.connection;
|
||||
this._serviceName = params.name;
|
||||
this._managerPath = params.objectPath;
|
||||
this._cancellable = params.cancellable;
|
||||
|
||||
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||
g_interface_name: ObjectManagerInfo.name,
|
||||
g_interface_info: ObjectManagerInfo,
|
||||
g_name: this._serviceName,
|
||||
g_object_path: this._managerPath,
|
||||
g_flags: Gio.DBusProxyFlags.NONE });
|
||||
|
||||
this._interfaceInfos = {};
|
||||
this._objects = {};
|
||||
this._interfaces = {};
|
||||
this._pendingProxies = [];
|
||||
this._onLoaded = params.onLoaded;
|
||||
|
||||
if (params.knownInterfaces)
|
||||
this._registerInterfaces(params.knownInterfaces);
|
||||
|
||||
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||
this._cancellable,
|
||||
Lang.bind(this, this._onManagerProxyLoaded));
|
||||
},
|
||||
|
||||
_addInterface: function(objectPath, interfaceName, onFinished) {
|
||||
let info = this._interfaceInfos[interfaceName];
|
||||
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||
g_name: this._serviceName,
|
||||
g_object_path: objectPath,
|
||||
g_interface_name: interfaceName,
|
||||
g_interface_info: info,
|
||||
g_flags: Gio.DBusProxyFlags.NONE });
|
||||
|
||||
this._pendingProxies.push(proxy);
|
||||
|
||||
proxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(initable, result) {
|
||||
let index = this._pendingProxies.indexOf(proxy);
|
||||
|
||||
if (index >= 0)
|
||||
this._pendingProxies.splice(index, 1);
|
||||
|
||||
let error = null;
|
||||
try {
|
||||
initable.init_finish(result);
|
||||
} catch(e) {
|
||||
logError(e, 'could not initialize proxy for interface ' + interfaceName);
|
||||
|
||||
if (onFinished)
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
let isNewObject;
|
||||
|
||||
if (!this._objects[objectPath]) {
|
||||
this._objects[objectPath] = {};
|
||||
isNewObject = true;
|
||||
} else {
|
||||
isNewObject = false;
|
||||
}
|
||||
|
||||
this._objects[objectPath][interfaceName] = proxy;
|
||||
|
||||
if (!this._interfaces[interfaceName])
|
||||
this._interfaces[interfaceName] = [];
|
||||
|
||||
this._interfaces[interfaceName].push(proxy);
|
||||
|
||||
if (isNewObject)
|
||||
this.emit('object-added', objectPath);
|
||||
|
||||
this.emit('interface-added', interfaceName, proxy);
|
||||
|
||||
if (onFinished)
|
||||
onFinished();
|
||||
}));
|
||||
},
|
||||
|
||||
_removeInterface: function(objectPath, interfaceName) {
|
||||
if (!this._objects[objectPath])
|
||||
return;
|
||||
|
||||
let proxy = this._objects[objectPath][interfaceName];
|
||||
|
||||
if (this._interfaces[interfaceName]) {
|
||||
let index = this._interfaces[interfaceName].indexOf(proxy);
|
||||
|
||||
if (index >= 0)
|
||||
this._interfaces[interfaceName].splice(index, 1);
|
||||
|
||||
if (this._interfaces[interfaceName].length == 0)
|
||||
delete this._interfaces[interfaceName];
|
||||
}
|
||||
|
||||
this.emit('interface-removed', interfaceName, proxy);
|
||||
|
||||
this._objects[objectPath][interfaceName] = null;
|
||||
|
||||
if (Object.keys(this._objects[objectPath]).length == 0) {
|
||||
delete this._objects[objectPath];
|
||||
this.emit('object-removed', objectPath);
|
||||
}
|
||||
},
|
||||
|
||||
_onManagerProxyLoaded: function(initable, result) {
|
||||
let error = null;
|
||||
try {
|
||||
initable.init_finish(result);
|
||||
} catch(e) {
|
||||
logError(e, 'could not initialize object manager for object ' + params.name);
|
||||
|
||||
if (this._onLoaded)
|
||||
this._onLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
this._managerProxy.connectSignal('InterfacesAdded',
|
||||
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
|
||||
let interfaceNames = Object.keys(interfaces);
|
||||
for (let i = 0; i < interfaceNames.length; i++)
|
||||
this._addInterface(objectPath, interfaceNames[i]);
|
||||
}));
|
||||
this._managerProxy.connectSignal('InterfacesRemoved',
|
||||
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
|
||||
for (let i = 0; i < interfaceNames.length; i++)
|
||||
this._removeInterface(objectPath, interfaceNames[i]);
|
||||
}));
|
||||
|
||||
|
||||
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
|
||||
if (!result) {
|
||||
if (error) {
|
||||
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
|
||||
}
|
||||
|
||||
if (this._onLoaded)
|
||||
this._onLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
let [objects] = result;
|
||||
|
||||
if (Object.keys(this._interfaceInfos).length == 0) {
|
||||
if (this._onLoaded)
|
||||
this._onLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
let numLoadInhibitors = 0;
|
||||
|
||||
// First inhibitor is to prevent onLoaded from getting
|
||||
// called until all interfaces have started being added.
|
||||
// Subsequent inhibitors are to prevent onLoaded from getting
|
||||
// called until all interfaces finish getting added.
|
||||
numLoadInhibitors++;
|
||||
let objectPaths = Object.keys(objects);
|
||||
for (let i = 0; i < objectPaths.length; i++) {
|
||||
let objectPath = objectPaths[i];
|
||||
let object = objects[objectPath];
|
||||
|
||||
let interfaceNames = Object.keys(object);
|
||||
for (let j = 0; j < interfaceNames.length; j++) {
|
||||
let interfaceName = interfaceNames[j];
|
||||
|
||||
numLoadInhibitors++;
|
||||
this._addInterface(objectPath,
|
||||
interfaceName,
|
||||
Lang.bind(this, function() {
|
||||
numLoadInhibitors--;
|
||||
|
||||
if (numLoadInhibitors == 0) {
|
||||
if (this._onLoaded)
|
||||
this._onLoaded();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
numLoadInhibitors--;
|
||||
|
||||
if (numLoadInhibitors == 0) {
|
||||
if (this._onLoaded)
|
||||
this._onLoaded();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_registerInterfaces: function(interfaces) {
|
||||
for (let i = 0; i < interfaces.length; i++) {
|
||||
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
|
||||
|
||||
this._interfaceInfos[info.name] = info;
|
||||
}
|
||||
},
|
||||
|
||||
getProxy: function(objectPath, interfaceName) {
|
||||
let object = this._objects[objectPath];
|
||||
|
||||
if (!object)
|
||||
return null;
|
||||
|
||||
return object[interfaceName];
|
||||
},
|
||||
|
||||
getProxiesForInterface: function(interfaceName) {
|
||||
let proxyList = this._interfaces[interfaceName];
|
||||
|
||||
if (!proxyList)
|
||||
return [];
|
||||
|
||||
return proxyList;
|
||||
},
|
||||
|
||||
getAllProxies: function() {
|
||||
let proxies = [];
|
||||
|
||||
let objectPaths = Object.keys(this._objects);
|
||||
for (let i = 0; i < objectPaths.length; i++) {
|
||||
let object = this._objects[objectPaths];
|
||||
|
||||
let interfaceNames = Object.keys(object);
|
||||
for (let j = 0; i < interfaceNames.length; i++) {
|
||||
let interfaceName = interfaceNames[i];
|
||||
if (object[interfaceName])
|
||||
proxies.push(object(interfaceName));
|
||||
}
|
||||
}
|
||||
|
||||
return proxies;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ObjectManager.prototype);
|
142
js/misc/smartcardManager.js
Normal file
142
js/misc/smartcardManager.js
Normal file
@ -0,0 +1,142 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const ObjectManager = imports.misc.objectManager;
|
||||
|
||||
const _SMARTCARD_SERVICE_DBUS_NAME = "org.gnome.SettingsDaemon.Smartcard";
|
||||
|
||||
const SmartcardManagerIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Manager">
|
||||
<method name="GetLoginToken">
|
||||
<arg name="token" type="o" direction="out"/>
|
||||
</method>
|
||||
<method name="GetInsertedTokens">
|
||||
<arg name="tokens" type="ao" direction="out"/>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
|
||||
<property name="Name" type="s" access="read"/>
|
||||
<property name="Driver" type="o" access="read"/>
|
||||
<property name="IsInserted" type="b" access="read"/>
|
||||
<property name="UsedToLogin" type="b" access="read"/>
|
||||
</interface>;
|
||||
|
||||
const SmartcardDriverIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Driver">
|
||||
<property name="Library" type="s" access="read"/>
|
||||
<property name="Description" type="s" access="read"/>
|
||||
</interface>;
|
||||
|
||||
let _smartcardManager = null;
|
||||
|
||||
function getSmartcardManager() {
|
||||
if (_smartcardManager == null)
|
||||
_smartcardManager = new SmartcardManager();
|
||||
|
||||
return _smartcardManager;
|
||||
}
|
||||
|
||||
const SmartcardManager = new Lang.Class({
|
||||
Name: 'SmartcardManager',
|
||||
_init: function() {
|
||||
this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
|
||||
name: _SMARTCARD_SERVICE_DBUS_NAME,
|
||||
objectPath: '/org/gnome/SettingsDaemon/Smartcard',
|
||||
knownInterfaces: [ SmartcardManagerIface,
|
||||
SmartcardTokenIface,
|
||||
SmartcardDriverIface ],
|
||||
onLoaded: Lang.bind(this, this._onLoaded) });
|
||||
this._insertedTokens = {};
|
||||
this._removedTokens = {};
|
||||
this._loginToken = null;
|
||||
},
|
||||
|
||||
_onLoaded: function() {
|
||||
let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
|
||||
|
||||
for (let i = 0; i < tokens.length; i++)
|
||||
this._addToken(tokens[i]);
|
||||
|
||||
this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||
this._addToken(proxy);
|
||||
}));
|
||||
|
||||
this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||
this._removeToken(proxy);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateToken: function(token) {
|
||||
let objectPath = token.get_object_path();
|
||||
|
||||
delete this._insertedTokens[objectPath];
|
||||
delete this._removedTokens[objectPath];
|
||||
|
||||
if (token.IsInserted)
|
||||
this._insertedTokens[objectPath] = token;
|
||||
else
|
||||
this._removedTokens[objectPath] = token;
|
||||
|
||||
if (token.UsedToLogin)
|
||||
this._loginToken = token;
|
||||
},
|
||||
|
||||
_addToken: function(token) {
|
||||
this._updateToken(token);
|
||||
|
||||
token.connect('g-properties-changed',
|
||||
Lang.bind(this, function(proxy, properties) {
|
||||
if ('IsInserted' in properties.deep_unpack()) {
|
||||
this._updateToken(token);
|
||||
|
||||
if (token.IsInserted) {
|
||||
this.emit('smartcard-inserted', token.Name);
|
||||
} else {
|
||||
this.emit('smartcard-removed', token.Name);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Emit a smartcard-inserted at startup if it's already plugged in
|
||||
if (token.IsInserted)
|
||||
this.emit('smartcard-inserted', token.Name);
|
||||
},
|
||||
|
||||
_removeToken: function(token) {
|
||||
let objectPath = token.get_object_path();
|
||||
|
||||
if (objectPath) {
|
||||
if (this._removedTokens[objectPath] == token) {
|
||||
delete this._removedTokens[objectPath];
|
||||
}
|
||||
|
||||
if (this._insertedTokens[objectPath] == token) {
|
||||
delete this._insertedTokens[objectPath];
|
||||
this.emit('smartcard-removed', token.Name);
|
||||
}
|
||||
}
|
||||
|
||||
token.disconnectAll();
|
||||
},
|
||||
|
||||
hasInsertedTokens: function() {
|
||||
return Object.keys(this._insertedTokens).length > 0;
|
||||
},
|
||||
|
||||
hasInsertedLoginToken: function() {
|
||||
if (!this._loginToken)
|
||||
return false;
|
||||
|
||||
if (!this._loginToken.IsInserted)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
Signals.addSignalMethods(SmartcardManager.prototype);
|
@ -436,8 +436,11 @@ const AppSwitcher = new Lang.Class({
|
||||
this._arrows = [];
|
||||
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
|
||||
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
|
||||
: null;
|
||||
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
|
||||
global.screen, null);
|
||||
global.screen, workspace);
|
||||
|
||||
// Construct the AppIcons, add to the popup
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
@ -447,7 +450,9 @@ const AppSwitcher = new Lang.Class({
|
||||
appIcon.cachedWindows = allWindows.filter(function(w) {
|
||||
return windowTracker.get_window_app (w) == appIcon.app;
|
||||
});
|
||||
this._addIcon(appIcon);
|
||||
if (workspace == null || appIcon.cachedWindows.length > 0) {
|
||||
this._addIcon(appIcon);
|
||||
}
|
||||
}
|
||||
|
||||
this._curApp = -1;
|
||||
|
84
js/ui/animation.js
Normal file
84
js/ui/animation.js
Normal file
@ -0,0 +1,84 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||
|
||||
const Animation = new Lang.Class({
|
||||
Name: 'Animation',
|
||||
|
||||
_init: function(filename, width, height, speed) {
|
||||
this.actor = new St.Bin();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this._speed = speed;
|
||||
|
||||
this._isLoaded = false;
|
||||
this._isPlaying = false;
|
||||
this._timeoutId = 0;
|
||||
this._frame = 0;
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
||||
Lang.bind(this, this._animationsLoaded));
|
||||
this.actor.set_child(this._animations);
|
||||
},
|
||||
|
||||
play: function() {
|
||||
if (this._isLoaded && this._timeoutId == 0) {
|
||||
if (this._frame == 0)
|
||||
this._showFrame(0);
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||
}
|
||||
|
||||
this._isPlaying = true;
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (this._timeoutId > 0) {
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
}
|
||||
|
||||
this._isPlaying = false;
|
||||
},
|
||||
|
||||
_showFrame: function(frame) {
|
||||
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
||||
if (oldFrameActor)
|
||||
oldFrameActor.hide();
|
||||
|
||||
this._frame = (frame % this._animations.get_n_children());
|
||||
|
||||
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
||||
if (newFrameActor)
|
||||
newFrameActor.show();
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
this._showFrame(this._frame + 1);
|
||||
return true;
|
||||
},
|
||||
|
||||
_animationsLoaded: function() {
|
||||
this._isLoaded = true;
|
||||
|
||||
if (this._isPlaying)
|
||||
this.play();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
|
||||
const AnimatedIcon = new Lang.Class({
|
||||
Name: 'AnimatedIcon',
|
||||
Extends: Animation,
|
||||
|
||||
_init: function(filename, size) {
|
||||
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||
}
|
||||
});
|
@ -358,17 +358,6 @@ const ControlsBoxLayout = Lang.Class({
|
||||
let totalSpacing = this.spacing * (childrenCount - 1);
|
||||
return [maxMinWidth * childrenCount + totalSpacing,
|
||||
maxNaturalWidth * childrenCount + totalSpacing];
|
||||
},
|
||||
|
||||
vfunc_set_container: function(container) {
|
||||
if(this._styleChangedId) {
|
||||
this._container.disconnect(this._styleChangedId);
|
||||
this._styleChangedId = 0;
|
||||
}
|
||||
if(container != null)
|
||||
this._styleChangedId = container.connect('style-changed', Lang.bind(this,
|
||||
function() { this.spacing = this._container.get_theme_node().get_length('spacing'); }));
|
||||
this._container = container;
|
||||
}
|
||||
});
|
||||
|
||||
@ -416,8 +405,9 @@ const AppDisplay = new Lang.Class({
|
||||
this.actor.add(this._viewStack, { expand: true });
|
||||
|
||||
let layout = new ControlsBoxLayout({ homogeneous: true });
|
||||
this._controls = new St.Widget({ style_class: 'app-view-controls' });
|
||||
this._controls.set_layout_manager(layout);
|
||||
this._controls = new St.Widget({ style_class: 'app-view-controls',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(this._controls);
|
||||
this.actor.add(new St.Bin({ child: this._controls }));
|
||||
|
||||
|
||||
|
@ -142,23 +142,20 @@ const BackgroundCache = new Lang.Class({
|
||||
cancellable: null,
|
||||
onFinished: null });
|
||||
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
if (this._pendingFileLoads[i].filename == params.filename &&
|
||||
this._pendingFileLoads[i].style == params.style) {
|
||||
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished });
|
||||
return;
|
||||
}
|
||||
}
|
||||
let fileLoad = { filename: params.filename,
|
||||
style: params.style,
|
||||
shouldCopy: false,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished,
|
||||
cancellable: new Gio.Cancellable(), };
|
||||
this._pendingFileLoads.push(fileLoad);
|
||||
|
||||
this._pendingFileLoads.push({ filename: params.filename,
|
||||
style: params.style,
|
||||
callers: [{ shouldCopy: false,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished }] });
|
||||
if (params.cancellable) {
|
||||
params.cancellable.connect(Lang.bind(this, function(c) {
|
||||
fileLoad.cancellable.cancel();
|
||||
}));
|
||||
}
|
||||
|
||||
let content = new Meta.Background({ meta_screen: global.screen,
|
||||
monitor: params.monitorIndex,
|
||||
@ -166,9 +163,19 @@ const BackgroundCache = new Lang.Class({
|
||||
|
||||
content.load_file_async(params.filename,
|
||||
params.style,
|
||||
params.cancellable,
|
||||
fileLoad.cancellable,
|
||||
Lang.bind(this,
|
||||
function(object, result) {
|
||||
if (fileLoad.cancellable.is_cancelled()) {
|
||||
if (params.cancellable && params.cancellable.is_cancelled()) {
|
||||
if (params.onFinished)
|
||||
params.onFinished(null);
|
||||
this._removePendingFileLoad(fileLoad);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
content.load_file_finish(result);
|
||||
|
||||
@ -178,22 +185,25 @@ const BackgroundCache = new Lang.Class({
|
||||
content = null;
|
||||
}
|
||||
|
||||
let needsCopy = false;
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
let pendingLoad = this._pendingFileLoads[i];
|
||||
if (pendingLoad.filename != params.filename ||
|
||||
pendingLoad.style != params.style)
|
||||
continue;
|
||||
|
||||
for (let j = 0; j < pendingLoad.callers.length; j++) {
|
||||
if (pendingLoad.callers[j].onFinished) {
|
||||
if (content && pendingLoad.callers[j].shouldCopy) {
|
||||
content = object.copy(pendingLoad.callers[j].monitorIndex,
|
||||
pendingLoad.callers[j].effects);
|
||||
if (pendingLoad.cancellable.is_cancelled())
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
pendingLoad.callers[j].onFinished(content);
|
||||
pendingLoad.cancellable.cancel();
|
||||
if (pendingLoad.onFinished) {
|
||||
if (content && needsCopy) {
|
||||
content = object.copy(pendingLoad.monitorIndex,
|
||||
pendingLoad.effects);
|
||||
}
|
||||
|
||||
needsCopy = true;
|
||||
pendingLoad.onFinished(content);
|
||||
}
|
||||
|
||||
this._pendingFileLoads.splice(i, 1);
|
||||
@ -201,6 +211,15 @@ const BackgroundCache = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_removePendingFileLoad: function(fileLoad) {
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
if (this._pendingFileLoads[i].cancellable == fileLoad.cancellable) {
|
||||
this._pendingFileLoads.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getImageContent: function(params) {
|
||||
params = Params.parse(params, { monitorIndex: 0,
|
||||
style: null,
|
||||
@ -571,7 +590,16 @@ const Background = new Lang.Class({
|
||||
}
|
||||
|
||||
let uri = this._settings.get_string(PICTURE_URI_KEY);
|
||||
let filename = Gio.File.new_for_uri(uri).get_path();
|
||||
let filename;
|
||||
if (GLib.uri_parse_scheme(uri) != null)
|
||||
filename = Gio.File.new_for_uri(uri).get_path();
|
||||
else
|
||||
filename = uri;
|
||||
|
||||
if (!filename) {
|
||||
this._setLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
this._loadFile(filename);
|
||||
},
|
||||
|
@ -589,12 +589,12 @@ const BoxPointer = new Lang.Class({
|
||||
return St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
|
||||
if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width &&
|
||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||
return St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
if (sourceAllocation.y1 - boxWidth < monitor.x &&
|
||||
if (sourceAllocation.x1 - boxWidth < monitor.x &&
|
||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||
return St.Side.LEFT;
|
||||
break;
|
||||
|
@ -31,7 +31,7 @@ function shouldAutorunMount(mount, forTransient) {
|
||||
if (!volume || (!volume.allowAutorun && forTransient))
|
||||
return false;
|
||||
|
||||
if (!root.is_native() || isMountRootHidden(root))
|
||||
if (root.is_native() && isMountRootHidden(root))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -18,7 +18,7 @@ const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
// See Notification.appendMessage
|
||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
||||
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
|
||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
||||
const SCROLLBACK_RECENT_LENGTH = 20;
|
||||
const SCROLLBACK_IDLE_LENGTH = 5;
|
||||
@ -967,7 +967,8 @@ const ChatNotification = new Lang.Class({
|
||||
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'],
|
||||
childProps: { expand: true, x_fill: false },
|
||||
childProps: { expand: true, x_fill: false,
|
||||
x_align: St.Align.END },
|
||||
noTimestamp: true,
|
||||
timestamp: lastMessageTime });
|
||||
|
||||
|
@ -58,14 +58,10 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
},
|
||||
|
||||
focusGroup: function(item, timestamp) {
|
||||
if (item.focusCallback) {
|
||||
if (item.focusCallback)
|
||||
item.focusCallback(timestamp);
|
||||
} else {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
|
||||
else
|
||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
}
|
||||
},
|
||||
|
||||
// Sort the items into a consistent order; panel first, tray last,
|
||||
@ -136,8 +132,6 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_focusWindows: function(timestamp) {
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
global.stage.key_focus = null;
|
||||
global.screen.focus_default_window(timestamp);
|
||||
}
|
||||
});
|
||||
|
@ -153,15 +153,14 @@ const DateMenuButton = new Lang.Class({
|
||||
this._openClocksItem.actor.visible = visible &&
|
||||
(this._getClockApp() != null);
|
||||
this._separator.visible = visible;
|
||||
this._eventList.actor.visible = visible;
|
||||
if (visible) {
|
||||
let alignment = 0.25;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
alignment = 1.0 - alignment;
|
||||
this.menu._arrowAlignment = alignment;
|
||||
this._eventList.actor.get_parent().show();
|
||||
let alignment = 0.25;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
alignment = 1.0 - alignment;
|
||||
this.menu._arrowAlignment = alignment;
|
||||
} else {
|
||||
this.menu._arrowAlignment = 0.5;
|
||||
this._eventList.actor.get_parent().hide();
|
||||
this.menu._arrowAlignment = 0.5;
|
||||
}
|
||||
},
|
||||
|
||||
|
109
js/ui/dnd.js
109
js/ui/dnd.js
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const St = imports.gi.St;
|
||||
const Lang = imports.lang;
|
||||
@ -358,60 +359,65 @@ const _Draggable = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_updateDragHover : function () {
|
||||
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
|
||||
this._dragX, this._dragY);
|
||||
let dragEvent = {
|
||||
x: this._dragX,
|
||||
y: this._dragY,
|
||||
dragActor: this._dragActor,
|
||||
source: this.actor._delegate,
|
||||
targetActor: target
|
||||
};
|
||||
for (let i = 0; i < dragMonitors.length; i++) {
|
||||
let motionFunc = dragMonitors[i].dragMotion;
|
||||
if (motionFunc) {
|
||||
let result = motionFunc(dragEvent);
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (target) {
|
||||
if (target._delegate && target._delegate.handleDragOver) {
|
||||
let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);
|
||||
// We currently loop through all parents on drag-over even if one of the children has handled it.
|
||||
// We can check the return value of the function and break the loop if it's true if we don't want
|
||||
// to continue checking the parents.
|
||||
let result = target._delegate.handleDragOver(this.actor._delegate,
|
||||
this._dragActor,
|
||||
targX,
|
||||
targY,
|
||||
0);
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
target = target.get_parent();
|
||||
}
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
return false;
|
||||
},
|
||||
|
||||
_queueUpdateDragHover: function() {
|
||||
if (this._updateHoverId)
|
||||
GLib.source_remove(this._updateHoverId);
|
||||
|
||||
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
|
||||
Lang.bind(this, this._updateDragHover));
|
||||
},
|
||||
|
||||
_updateDragPosition : function (event) {
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
this._dragX = stageX;
|
||||
this._dragY = stageY;
|
||||
this._dragActor.set_position(stageX + this._dragOffsetX,
|
||||
stageY + this._dragOffsetY);
|
||||
|
||||
// If we are dragging, update the position
|
||||
if (this._dragActor) {
|
||||
this._dragActor.set_position(stageX + this._dragOffsetX,
|
||||
stageY + this._dragOffsetY);
|
||||
|
||||
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
|
||||
stageX, stageY);
|
||||
|
||||
// We call observers only once per motion with the innermost
|
||||
// target actor. If necessary, the observer can walk the
|
||||
// parent itself.
|
||||
let dragEvent = {
|
||||
x: stageX,
|
||||
y: stageY,
|
||||
dragActor: this._dragActor,
|
||||
source: this.actor._delegate,
|
||||
targetActor: target
|
||||
};
|
||||
for (let i = 0; i < dragMonitors.length; i++) {
|
||||
let motionFunc = dragMonitors[i].dragMotion;
|
||||
if (motionFunc) {
|
||||
let result = motionFunc(dragEvent);
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (target) {
|
||||
if (target._delegate && target._delegate.handleDragOver) {
|
||||
let [r, targX, targY] = target.transform_stage_point(stageX, stageY);
|
||||
// We currently loop through all parents on drag-over even if one of the children has handled it.
|
||||
// We can check the return value of the function and break the loop if it's true if we don't want
|
||||
// to continue checking the parents.
|
||||
let result = target._delegate.handleDragOver(this.actor._delegate,
|
||||
this._dragActor,
|
||||
targX,
|
||||
targY,
|
||||
event.get_time());
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
target = target.get_parent();
|
||||
}
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
}
|
||||
|
||||
this._queueUpdateDragHover();
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -511,6 +517,11 @@ const _Draggable = new Lang.Class({
|
||||
},
|
||||
|
||||
_cancelDrag: function(eventTime) {
|
||||
if (this._updateHoverId) {
|
||||
GLib.source_remove(this._updateHoverId);
|
||||
this._updateHoverId = 0;
|
||||
}
|
||||
|
||||
this.emit('drag-cancelled', eventTime);
|
||||
this._dragInProgress = false;
|
||||
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
||||
|
@ -10,6 +10,7 @@ const Clutter = imports.gi.Clutter;;
|
||||
const Gettext = imports.gettext;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
@ -39,6 +40,22 @@ function _patchContainerClass(containerClass) {
|
||||
};
|
||||
}
|
||||
|
||||
function _patchLayoutClass(layoutClass, styleProps) {
|
||||
if (styleProps)
|
||||
layoutClass.prototype.hookup_style = function(container) {
|
||||
container.connect('style-changed', Lang.bind(this, function() {
|
||||
let node = container.get_theme_node();
|
||||
for (let prop in styleProps)
|
||||
this[prop] = node.get_length(styleProps[prop]);
|
||||
}));
|
||||
};
|
||||
layoutClass.prototype.child_set = function(actor, props) {
|
||||
let meta = this.get_child_meta(actor.get_parent(), actor);
|
||||
for (let prop in props)
|
||||
meta[prop] = props[prop];
|
||||
};
|
||||
}
|
||||
|
||||
function _makeLoggingFunc(func) {
|
||||
return function() {
|
||||
return func([].join.call(arguments, ', '));
|
||||
@ -60,6 +77,12 @@ function init() {
|
||||
_patchContainerClass(St.BoxLayout);
|
||||
_patchContainerClass(St.Table);
|
||||
|
||||
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
|
||||
column_spacing: 'spacing-columns' });
|
||||
_patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
|
||||
column_spacing: 'spacing-columns' });
|
||||
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
|
||||
|
||||
Clutter.Actor.prototype.toString = function() {
|
||||
return St.describe_actor(this);
|
||||
};
|
||||
|
@ -32,13 +32,9 @@ const GrabHelper = new Lang.Class({
|
||||
|
||||
this._actors = [];
|
||||
this._capturedEventId = 0;
|
||||
this._keyFocusNotifyId = 0;
|
||||
this._focusWindowChangedId = 0;
|
||||
this._ignoreRelease = false;
|
||||
this._isUngrabbingCount = 0;
|
||||
|
||||
this._modalCount = 0;
|
||||
this._grabFocusCount = 0;
|
||||
},
|
||||
|
||||
// addActor:
|
||||
@ -118,38 +114,36 @@ const GrabHelper = new Lang.Class({
|
||||
// grab:
|
||||
// @params: A bunch of parameters, see below
|
||||
//
|
||||
// Grabs the mouse and keyboard, according to the GrabHelper's
|
||||
// parameters. If @newFocus is not %null, then the keyboard focus
|
||||
// is moved to the first #StWidget:can-focus widget inside it.
|
||||
// The general effect of a "grab" is to ensure that the passed in actor
|
||||
// and all actors inside the grab get exclusive control of the mouse and
|
||||
// keyboard, with the grab automatically being dropped if the user tries
|
||||
// to dismiss it. The actor is passed in through @params.actor.
|
||||
//
|
||||
// The grab will automatically be dropped if:
|
||||
// - The user clicks outside the grabbed actors
|
||||
// - The user types Escape
|
||||
// - The keyboard focus is moved outside the grabbed actors
|
||||
// - A window is focused
|
||||
// grab() can be called multiple times, with the scope of the grab being
|
||||
// changed to a different actor every time. A nested grab does not have
|
||||
// to have its grabbed actor inside the parent grab actors.
|
||||
//
|
||||
// If @params.actor is not null, then it will be focused as the
|
||||
// new actor. If you attempt to grab an already focused actor, the
|
||||
// request to be focused will be ignored. The actor will not be
|
||||
// added to the grab stack, so do not call a paired ungrab().
|
||||
// Grabs can be automatically dropped if the user tries to dismiss it
|
||||
// in one of two ways: the user clicking outside the currently grabbed
|
||||
// actor, or the user typing the Escape key.
|
||||
//
|
||||
// If @params contains { modal: true }, then grab() will push a modal
|
||||
// on the owner of the GrabHelper. As long as there is at least one
|
||||
// { modal: true } actor on the grab stack, the grab will be kept.
|
||||
// When the last { modal: true } actor is ungrabbed, then the modal
|
||||
// will be dropped. A modal grab can fail if there is already a grab
|
||||
// in effect from aother application; in this case the function returns
|
||||
// false and nothing happens. Non-modal grabs can never fail.
|
||||
// If the user clicks outside the grabbed actors, and the clicked on
|
||||
// actor is part of a previous grab in the stack, grabs will be popped
|
||||
// until that grab is active. However, the click event will not be
|
||||
// replayed to the actor.
|
||||
//
|
||||
// If @params contains { grabFocus: true }, then if you call grab()
|
||||
// while the shell is outside the overview, it will set the stage
|
||||
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
|
||||
// revert it back, and re-focus the previously-focused window (if
|
||||
// another window hasn't been explicitly focused before then).
|
||||
// If the user types the Escape key, one grab from the grab stack will
|
||||
// be popped.
|
||||
//
|
||||
// When a grab is popped by user interacting as described above, if you
|
||||
// pass a callback as @params.onUngrab, it will be called with %true.
|
||||
//
|
||||
// If @params.focus is not null, we'll set the key focus directly
|
||||
// to that actor instead of navigating in @params.actor. This is for
|
||||
// use cases like menus, where we want to grab the menu actor, but keep
|
||||
// focus on the clicked on menu item.
|
||||
grab: function(params) {
|
||||
params = Params.parse(params, { actor: null,
|
||||
modal: false,
|
||||
grabFocus: false,
|
||||
focus: null,
|
||||
onUngrab: null });
|
||||
|
||||
@ -162,24 +156,18 @@ const GrabHelper = new Lang.Class({
|
||||
|
||||
params.savedFocus = focus;
|
||||
|
||||
if (params.modal && !this._takeModalGrab())
|
||||
return false;
|
||||
|
||||
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
|
||||
if (!this._takeModalGrab())
|
||||
return false;
|
||||
|
||||
this._grabStack.push(params);
|
||||
|
||||
if (params.focus) {
|
||||
params.focus.grab_key_focus();
|
||||
} else if (newFocus && (hadFocus || params.grabFocus)) {
|
||||
} else if (newFocus && hadFocus) {
|
||||
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||
newFocus.grab_key_focus();
|
||||
}
|
||||
|
||||
if ((params.grabFocus || params.modal) && !this._capturedEventId)
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -188,6 +176,8 @@ const GrabHelper = new Lang.Class({
|
||||
if (firstGrab) {
|
||||
if (!Main.pushModal(this._owner, this._modalParams))
|
||||
return false;
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
}
|
||||
|
||||
this._modalCount++;
|
||||
@ -199,56 +189,15 @@ const GrabHelper = new Lang.Class({
|
||||
if (this._modalCount > 0)
|
||||
return;
|
||||
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
|
||||
this._ignoreRelease = false;
|
||||
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
},
|
||||
|
||||
_takeFocusGrab: function(hadFocus) {
|
||||
let firstGrab = (this._grabFocusCount == 0);
|
||||
if (firstGrab) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
|
||||
this._grabbedFromKeynav = hadFocus;
|
||||
this._preGrabInputMode = global.stage_input_mode;
|
||||
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
|
||||
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
|
||||
}
|
||||
|
||||
this._grabFocusCount++;
|
||||
return true;
|
||||
},
|
||||
|
||||
_releaseFocusGrab: function() {
|
||||
this._grabFocusCount--;
|
||||
if (this._grabFocusCount > 0)
|
||||
return;
|
||||
|
||||
if (this._keyFocusNotifyId > 0) {
|
||||
global.stage.disconnect(this._keyFocusNotifyId);
|
||||
this._keyFocusNotifyId = 0;
|
||||
}
|
||||
|
||||
if (this._focusWindowChangedId > 0) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
metaDisplay.disconnect(this._focusWindowChangedId);
|
||||
this._focusWindowChangedId = 0;
|
||||
}
|
||||
|
||||
let prePopInputMode = global.stage_input_mode;
|
||||
|
||||
if (this._grabbedFromKeynav) {
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
|
||||
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
}
|
||||
|
||||
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
|
||||
},
|
||||
|
||||
// ignoreRelease:
|
||||
//
|
||||
// Make sure that the next button release event evaluated by the
|
||||
@ -262,10 +211,14 @@ const GrabHelper = new Lang.Class({
|
||||
// ungrab:
|
||||
// @params: The parameters for the grab; see below.
|
||||
//
|
||||
// Pops an actor from the grab stack, potentially dropping the grab.
|
||||
// Pops @params.actor from the grab stack, potentially dropping
|
||||
// the grab. If the actor is not on the grab stack, this call is
|
||||
// ignored with no ill effects.
|
||||
//
|
||||
// If the actor that was popped from the grab stack was not the actor
|
||||
// That was passed in, this call is ignored.
|
||||
// If the actor is not at the top of the grab stack, grabs are
|
||||
// popped until the grabbed actor is at the top of the grab stack.
|
||||
// The onUngrab callback for every grab is called for every popped
|
||||
// grab with the parameter %false.
|
||||
ungrab: function(params) {
|
||||
params = Params.parse(params, { actor: this.currentGrab.actor,
|
||||
isUser: false });
|
||||
@ -274,14 +227,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (grabStackIndex < 0)
|
||||
return;
|
||||
|
||||
// We may get key focus changes when calling onUngrab, which
|
||||
// would cause an extra ungrab() on the next actor in the
|
||||
// stack, which is wrong. Ignore key focus changes during the
|
||||
// ungrab, and restore the saved key focus ourselves afterwards.
|
||||
// We use a count as ungrab() may be re-entrant, as onUngrab()
|
||||
// may ungrab additional actors.
|
||||
this._isUngrabbingCount++;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||
|
||||
@ -296,18 +241,7 @@ const GrabHelper = new Lang.Class({
|
||||
if (poppedGrab.onUngrab)
|
||||
poppedGrab.onUngrab(params.isUser);
|
||||
|
||||
if (poppedGrab.modal)
|
||||
this._releaseModalGrab();
|
||||
|
||||
if (poppedGrab.grabFocus)
|
||||
this._releaseFocusGrab();
|
||||
}
|
||||
|
||||
if (!this.grabbed && this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
|
||||
this._ignoreRelease = false;
|
||||
this._releaseModalGrab();
|
||||
}
|
||||
|
||||
if (hadFocus) {
|
||||
@ -315,8 +249,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (poppedGrab.savedFocus)
|
||||
poppedGrab.savedFocus.grab_key_focus();
|
||||
}
|
||||
|
||||
this._isUngrabbingCount--;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
@ -337,9 +269,6 @@ const GrabHelper = new Lang.Class({
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!button && this._modalCount == 0)
|
||||
return false;
|
||||
|
||||
if (this._isWithinGrabbedActor(event.get_source()))
|
||||
return false;
|
||||
|
||||
@ -356,21 +285,6 @@ const GrabHelper = new Lang.Class({
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._modalCount > 0;
|
||||
return true;
|
||||
},
|
||||
|
||||
_onKeyFocusChanged: function() {
|
||||
if (this._isUngrabbingCount > 0)
|
||||
return;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
if (!focus || !this._isWithinGrabbedActor(focus))
|
||||
this.ungrab({ isUser: true });
|
||||
},
|
||||
|
||||
_focusWindowChanged: function() {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
if (metaDisplay.focus_window != null)
|
||||
this.ungrab({ isUser: true });
|
||||
}
|
||||
});
|
||||
|
@ -528,17 +528,10 @@ const LayoutManager = new Lang.Class({
|
||||
get focusIndex() {
|
||||
let i = Main.layoutManager.primaryIndex;
|
||||
|
||||
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
|
||||
global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
|
||||
let focusActor = global.stage.key_focus;
|
||||
if (focusActor)
|
||||
i = this.findIndexForActor(focusActor);
|
||||
} else {
|
||||
let focusWindow = global.display.focus_window;
|
||||
if (focusWindow)
|
||||
i = focusWindow.get_monitor();
|
||||
}
|
||||
|
||||
if (global.stage.key_focus != null)
|
||||
i = this.findIndexForActor(global.stage.key_focus);
|
||||
else if (global.display.focus_window != null)
|
||||
i = global.display.focus_window.get_monitor();
|
||||
return i;
|
||||
},
|
||||
|
||||
@ -596,9 +589,13 @@ const LayoutManager = new Lang.Class({
|
||||
// screen. So, we set no_clear_hint at the end of the animation.
|
||||
|
||||
_prepareStartupAnimation: function() {
|
||||
// Set ourselves to FULLSCREEN input mode while the animation is running
|
||||
// so events don't get delivered to X11 windows (which are distorted by the animation)
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
// During the initial transition, add a simple actor to block all events,
|
||||
// so they don't get delivered to X11 windows that have been transformed.
|
||||
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height,
|
||||
reactive: true });
|
||||
this.addChrome(this._coverPane);
|
||||
|
||||
if (Main.sessionMode.isGreeter) {
|
||||
this.panelBox.translation_y = -this.panelBox.height;
|
||||
@ -668,7 +665,8 @@ const LayoutManager = new Lang.Class({
|
||||
// we no longer need to clear the stage
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
||||
this._coverPane.destroy();
|
||||
this._coverPane = null;
|
||||
|
||||
this._systemBackground.actor.destroy();
|
||||
this._systemBackground = null;
|
||||
@ -749,6 +747,8 @@ const LayoutManager = new Lang.Class({
|
||||
// and shown otherwise)
|
||||
addChrome: function(actor, params) {
|
||||
this.uiGroup.add_actor(actor);
|
||||
if (this.uiGroup.contains(global.top_window_group))
|
||||
this.uiGroup.set_child_below_sibling(actor, global.top_window_group);
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
|
@ -849,8 +849,9 @@ const LookingGlass = new Lang.Class({
|
||||
this._updateFont();
|
||||
|
||||
// We want it to appear to slide out from underneath the panel
|
||||
Main.layoutManager.panelBox.add_actor(this.actor);
|
||||
this.actor.lower_bottom();
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
Main.uiGroup.set_child_below_sibling(this.actor,
|
||||
Main.layoutManager.panelBox);
|
||||
Main.layoutManager.panelBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._queueResize));
|
||||
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
||||
@ -1072,7 +1073,7 @@ const LookingGlass = new Lang.Class({
|
||||
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
|
||||
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
|
||||
this.actor.x = (primary.width - myWidth) / 2;
|
||||
this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
|
||||
this._hiddenY = Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
|
||||
this._targetY = this._hiddenY + myHeight;
|
||||
this.actor.y = this._hiddenY;
|
||||
this.actor.width = myWidth;
|
||||
|
@ -356,8 +356,6 @@ function pushModal(actor, params) {
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
}
|
||||
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
||||
|
||||
modalCount += 1;
|
||||
let actorDestroyId = actor.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
@ -406,7 +404,6 @@ function popModal(actor, timestamp) {
|
||||
if (focusIndex < 0) {
|
||||
global.stage.set_key_focus(null);
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
|
||||
throw new Error('incorrect pop');
|
||||
@ -454,7 +451,6 @@ function popModal(actor, timestamp) {
|
||||
return;
|
||||
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
}
|
||||
|
@ -97,6 +97,65 @@ function _fixMarkup(text, allowMarkup) {
|
||||
return GLib.markup_escape_text(text, -1);
|
||||
}
|
||||
|
||||
const FocusGrabber = new Lang.Class({
|
||||
Name: 'FocusGrabber',
|
||||
|
||||
_init: function(actor) {
|
||||
this._actor = actor;
|
||||
this._prevKeyFocusActor = null;
|
||||
this._focusActorChangedId = 0;
|
||||
this._focused = false;
|
||||
},
|
||||
|
||||
grabFocus: function() {
|
||||
if (this._focused)
|
||||
return;
|
||||
|
||||
this._prevFocusedWindow = global.display.focus_window;
|
||||
this._prevKeyFocusActor = global.stage.get_key_focus();
|
||||
|
||||
this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged));
|
||||
|
||||
if (!this._actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||
this._actor.grab_key_focus();
|
||||
|
||||
this._focused = true;
|
||||
},
|
||||
|
||||
_focusUngrabbed: function() {
|
||||
if (!this._focused)
|
||||
return false;
|
||||
|
||||
if (this._focusActorChangedId > 0) {
|
||||
global.stage.disconnect(this._focusActorChangedId);
|
||||
this._focusActorChangedId = 0;
|
||||
}
|
||||
|
||||
this._focused = false;
|
||||
return true;
|
||||
},
|
||||
|
||||
_focusActorChanged: function() {
|
||||
let focusedActor = global.stage.get_key_focus();
|
||||
if (!focusedActor || !this._actor.contains(focusedActor))
|
||||
this._focusUngrabbed();
|
||||
},
|
||||
|
||||
ungrabFocus: function() {
|
||||
if (!this._focusUngrabbed())
|
||||
return;
|
||||
|
||||
if (this._prevKeyFocusActor) {
|
||||
global.stage.set_key_focus(this._prevKeyFocusActor);
|
||||
this._prevKeyFocusActor = null;
|
||||
} else {
|
||||
let focusedActor = global.stage.get_key_focus();
|
||||
if (focusedActor && this._actor.contains(focusedActor))
|
||||
global.stage.set_key_focus(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const URLHighlighter = new Lang.Class({
|
||||
Name: 'URLHighlighter',
|
||||
|
||||
@ -1365,6 +1424,10 @@ const SummaryItem = new Lang.Class({
|
||||
this.notificationStackWidget.add_actor(this.notificationStackView);
|
||||
|
||||
this.closeButton = Util.makeCloseButton();
|
||||
this.closeButton.connect('clicked', Lang.bind(this, function() {
|
||||
source.destroy();
|
||||
source.emit('done-displaying-content');
|
||||
}));
|
||||
this.notificationStackWidget.add_actor(this.closeButton);
|
||||
this._stackedNotifications = [];
|
||||
|
||||
@ -1574,6 +1637,7 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
|
||||
this._notificationWidget.add_actor(this._notificationBin);
|
||||
this._notificationWidget.hide();
|
||||
this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget);
|
||||
this._notificationQueue = [];
|
||||
this._notification = null;
|
||||
this._notificationClickedId = 0;
|
||||
@ -1623,7 +1687,6 @@ const MessageTray = new Lang.Class({
|
||||
{ keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY });
|
||||
this._grabHelper.addActor(this._summaryBoxPointer.actor);
|
||||
this._grabHelper.addActor(this.actor);
|
||||
this._grabHelper.addActor(this._notificationWidget);
|
||||
|
||||
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
|
||||
|
||||
@ -1749,7 +1812,6 @@ const MessageTray = new Lang.Class({
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
this._contextMenu.setPosition(Math.round(x), Math.round(y));
|
||||
this._grabHelper.grab({ actor: this._contextMenu.actor,
|
||||
modal: true,
|
||||
onUngrab: Lang.bind(this, function () {
|
||||
this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
|
||||
})
|
||||
@ -1857,9 +1919,8 @@ const MessageTray = new Lang.Class({
|
||||
_closeNotification: function() {
|
||||
if (this._notificationState == State.SHOWN) {
|
||||
this._closeButton.hide();
|
||||
this._notificationClosed = true;
|
||||
this._updateState();
|
||||
this._notificationClosed = false;
|
||||
this._notification.emit('done-displaying');
|
||||
this._notification.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
@ -2316,7 +2377,6 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_showTray: function() {
|
||||
if (!this._grabHelper.grab({ actor: this.actor,
|
||||
modal: true,
|
||||
onUngrab: Lang.bind(this, this._escapeTray) })) {
|
||||
this._traySummoned = false;
|
||||
return false;
|
||||
@ -2520,19 +2580,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_hideNotification: function() {
|
||||
// HACK!
|
||||
// There seems to be a reentrancy issue in calling .ungrab() here,
|
||||
// which causes _updateState to be called before _notificationState
|
||||
// becomes HIDING. That hides the notification again, nullifying the
|
||||
// object but not setting _notificationState (and that's the weird part)
|
||||
// As then _notificationState is stuck into SHOWN but _notification
|
||||
// is null, every new _updateState fails and the message tray is
|
||||
// lost forever.
|
||||
//
|
||||
// See more at https://bugzilla.gnome.org/show_bug.cgi?id=683986
|
||||
this._notificationState = State.HIDING;
|
||||
|
||||
this._grabHelper.ungrab({ actor: this._notification.actor });
|
||||
this._notificationFocusGrabber.ungrabFocus();
|
||||
|
||||
if (this._notificationExpandedId) {
|
||||
this._notification.disconnect(this._notificationExpandedId);
|
||||
@ -2597,16 +2645,16 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_expandNotification: function(autoExpanding) {
|
||||
// Don't focus notifications that are auto-expanding.
|
||||
if (!autoExpanding)
|
||||
this._ensureNotificationFocused();
|
||||
|
||||
if (!this._notificationExpandedId)
|
||||
this._notificationExpandedId =
|
||||
this._notification.connect('expanded',
|
||||
Lang.bind(this, this._onNotificationExpanded));
|
||||
// Don't animate changes in notifications that are auto-expanding.
|
||||
this._notification.expand(!autoExpanding);
|
||||
|
||||
// Don't focus notifications that are auto-expanding.
|
||||
if (!autoExpanding)
|
||||
this._ensureNotificationFocused();
|
||||
},
|
||||
|
||||
_onNotificationExpanded: function() {
|
||||
@ -2630,8 +2678,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_ensureNotificationFocused: function() {
|
||||
this._grabHelper.grab({ actor: this._notification.actor,
|
||||
grabFocus: true });
|
||||
this._notificationFocusGrabber.grabFocus();
|
||||
},
|
||||
|
||||
_onSourceDoneDisplayingContent: function(source, closeTray) {
|
||||
@ -2654,11 +2701,9 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
let closeButton = summaryItem.closeButton;
|
||||
closeButton.show();
|
||||
this._summaryBoxPointerCloseClickedId = closeButton.connect('clicked', Lang.bind(this, this._hideSummaryBoxPointer));
|
||||
summaryItem.prepareNotificationStackForShowing();
|
||||
} else if (this._clickedSummaryItemMouseButton == 3) {
|
||||
child = summaryItem.rightClickMenu;
|
||||
this._summaryBoxPointerCloseClickedId = 0;
|
||||
}
|
||||
|
||||
// If the user clicked the middle mouse button, or the item
|
||||
@ -2674,7 +2719,6 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
this._summaryBoxPointer.bin.child = child;
|
||||
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
|
||||
modal: true,
|
||||
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
|
||||
|
||||
this._summaryBoxPointer.actor.opacity = 0;
|
||||
@ -2753,10 +2797,7 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
|
||||
this._summaryBoxPointerContentUpdatedId = 0;
|
||||
}
|
||||
if (this._summaryBoxPointerCloseClickedId != 0) {
|
||||
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
|
||||
this._summaryBoxPointerCloseClickedId = 0;
|
||||
}
|
||||
|
||||
if (this._sourceDoneDisplayingId) {
|
||||
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
|
||||
this._sourceDoneDisplayingId = 0;
|
||||
|
@ -14,6 +14,7 @@ const Atk = imports.gi.Atk;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const Animation = imports.ui.animation;
|
||||
const Layout = imports.ui.layout;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
@ -78,9 +79,8 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
if (params.styleClass != null) {
|
||||
if (params.styleClass != null)
|
||||
this.dialogLayout.add_style_class_name(params.styleClass);
|
||||
}
|
||||
|
||||
if (!this._shellReactive) {
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
@ -187,10 +187,8 @@ const ModalDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
placeSpinner: function(layoutInfo) {
|
||||
/* This is here because of recursive imports */
|
||||
const Panel = imports.ui.panel;
|
||||
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||
this._workSpinner = new Panel.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||
this._workSpinner.actor.opacity = 0;
|
||||
this._workSpinner.actor.show();
|
||||
|
||||
@ -358,8 +356,9 @@ const ModalDialog = new Lang.Class({
|
||||
if (this._savedKeyFocus) {
|
||||
this._savedKeyFocus.grab_key_focus();
|
||||
this._savedKeyFocus = null;
|
||||
} else
|
||||
} else {
|
||||
this._initialKeyFocus.grab_key_focus();
|
||||
}
|
||||
|
||||
if (!this._shellReactive)
|
||||
this._eventBlocker.lower_bottom();
|
||||
|
@ -717,8 +717,8 @@ const Source = new Lang.Class({
|
||||
this.notifications.length > 0)
|
||||
return false;
|
||||
|
||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
|
||||
global.disconnect(id);
|
||||
let id = global.stage.connect('deactivate', Lang.bind(this, function () {
|
||||
global.stage.disconnect(id);
|
||||
this.trayIcon.click(event);
|
||||
}));
|
||||
|
||||
|
@ -158,23 +158,26 @@ const OsdWindow = new Lang.Class({
|
||||
return;
|
||||
|
||||
Mainloop.source_remove(this._hideTimeoutId);
|
||||
this._hideTimeoutId = 0;
|
||||
this._hide();
|
||||
},
|
||||
|
||||
_hide: function() {
|
||||
this._hideTimeoutId = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, this._reset) });
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._reset();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this.actor.hide();
|
||||
this.setLabel(null);
|
||||
this.setLevel(null);
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
|
@ -148,7 +148,7 @@ const Overview = new Lang.Class({
|
||||
// Dash elements, or mouseover handlers in the workspaces.
|
||||
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||
reactive: true });
|
||||
this._overview.add_actor(this._coverPane);
|
||||
this._stack.add_actor(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
||||
|
||||
this._stack.add_actor(this._overview);
|
||||
|
@ -15,6 +15,7 @@ const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
|
||||
const Animation = imports.ui.animation;
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const DND = imports.ui.dnd;
|
||||
@ -29,7 +30,6 @@ const PANEL_ICON_SIZE = 24;
|
||||
|
||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||
|
||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||
const SPINNER_ANIMATION_TIME = 0.2;
|
||||
|
||||
// To make sure the panel corners blend nicely with the panel,
|
||||
@ -75,81 +75,6 @@ function _unpremultiply(color) {
|
||||
blue: blue, alpha: color.alpha });
|
||||
};
|
||||
|
||||
const Animation = new Lang.Class({
|
||||
Name: 'Animation',
|
||||
|
||||
_init: function(filename, width, height, speed) {
|
||||
this.actor = new St.Bin();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this._speed = speed;
|
||||
|
||||
this._isLoaded = false;
|
||||
this._isPlaying = false;
|
||||
this._timeoutId = 0;
|
||||
this._frame = 0;
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
||||
Lang.bind(this, this._animationsLoaded));
|
||||
this.actor.set_child(this._animations);
|
||||
},
|
||||
|
||||
play: function() {
|
||||
if (this._isLoaded && this._timeoutId == 0) {
|
||||
if (this._frame == 0)
|
||||
this._showFrame(0);
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||
}
|
||||
|
||||
this._isPlaying = true;
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (this._timeoutId > 0) {
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
}
|
||||
|
||||
this._isPlaying = false;
|
||||
},
|
||||
|
||||
_showFrame: function(frame) {
|
||||
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
||||
if (oldFrameActor)
|
||||
oldFrameActor.hide();
|
||||
|
||||
this._frame = (frame % this._animations.get_n_children());
|
||||
|
||||
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
||||
if (newFrameActor)
|
||||
newFrameActor.show();
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
this._showFrame(this._frame + 1);
|
||||
return true;
|
||||
},
|
||||
|
||||
_animationsLoaded: function() {
|
||||
this._isLoaded = true;
|
||||
|
||||
if (this._isPlaying)
|
||||
this.play();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
|
||||
const AnimatedIcon = new Lang.Class({
|
||||
Name: 'AnimatedIcon',
|
||||
Extends: Animation,
|
||||
|
||||
_init: function(filename, size) {
|
||||
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||
}
|
||||
});
|
||||
|
||||
const TextShadower = new Lang.Class({
|
||||
Name: 'TextShadower',
|
||||
|
||||
@ -360,7 +285,7 @@ const AppMenuButton = new Lang.Class({
|
||||
if (!success || this._spinnerIcon == icon)
|
||||
return;
|
||||
this._spinnerIcon = icon;
|
||||
this._spinner = new AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
||||
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
||||
this._container.add_actor(this._spinner.actor);
|
||||
this._spinner.actor.hide();
|
||||
this._spinner.actor.lower_bottom();
|
||||
@ -372,13 +297,16 @@ const AppMenuButton = new Lang.Class({
|
||||
this._updateIconBoxClip();
|
||||
},
|
||||
|
||||
_syncIcon: function() {
|
||||
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
|
||||
this._iconBox.set_child(icon);
|
||||
},
|
||||
|
||||
_onIconThemeChanged: function() {
|
||||
if (this._iconBox.child == null)
|
||||
return;
|
||||
|
||||
this._iconBox.child.destroy();
|
||||
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
this._iconBox.set_child(icon);
|
||||
this._syncIcon();
|
||||
},
|
||||
|
||||
_updateIconBoxClip: function() {
|
||||
@ -524,7 +452,7 @@ const AppMenuButton = new Lang.Class({
|
||||
// If the app has just lost focus to the panel, pretend
|
||||
// nothing happened; otherwise you can't keynav to the
|
||||
// app menu.
|
||||
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
|
||||
if (global.stage.key_focus != null)
|
||||
return;
|
||||
}
|
||||
this._sync();
|
||||
@ -601,12 +529,10 @@ const AppMenuButton = new Lang.Class({
|
||||
}
|
||||
|
||||
this._targetApp = targetApp;
|
||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
|
||||
this._label.setText(targetApp.get_name());
|
||||
this.setName(targetApp.get_name());
|
||||
|
||||
this._iconBox.set_child(icon);
|
||||
this._syncIcon();
|
||||
this._iconBox.show();
|
||||
|
||||
if (targetApp.get_state() == Shell.AppState.STARTING ||
|
||||
@ -932,7 +858,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||
'system': imports.ui.status.system.Indicator,
|
||||
};
|
||||
|
||||
if (Config.HAVE_BLUETOOTH)
|
||||
|
@ -110,6 +110,7 @@ const Button = new Lang.Class({
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
|
||||
|
||||
if (dontCreateMenu)
|
||||
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
||||
@ -183,6 +184,14 @@ const Button = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_onVisibilityChanged: function() {
|
||||
if (!this.menu)
|
||||
return;
|
||||
|
||||
if (!this.actor.visible)
|
||||
this.menu.close();
|
||||
},
|
||||
|
||||
_onMenuKeyPress: function(actor, event) {
|
||||
if (global.focus_manager.navigate_from_event(event))
|
||||
return true;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,6 @@ const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const KEY_FILE_GROUP = 'Shell Search Provider';
|
||||
@ -60,108 +59,114 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
||||
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
|
||||
|
||||
function loadRemoteSearchProviders(addProviderCallback) {
|
||||
let data = { loadedProviders: [],
|
||||
objectPaths: {},
|
||||
addProviderCallback: addProviderCallback };
|
||||
FileUtils.collectFromDatadirsAsync('search-providers',
|
||||
{ loadedCallback: remoteProvidersLoaded,
|
||||
processFile: loadRemoteSearchProvider,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
let objectPaths = {};
|
||||
let loadedProviders = [];
|
||||
|
||||
function loadRemoteSearchProvider(file, info, data) {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
let path = file.get_path();
|
||||
function loadRemoteSearchProvider(file) {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
let path = file.get_path();
|
||||
|
||||
try {
|
||||
keyfile.load_from_file(path, 0);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keyfile.has_group(KEY_FILE_GROUP))
|
||||
return;
|
||||
|
||||
let remoteProvider;
|
||||
try {
|
||||
let group = KEY_FILE_GROUP;
|
||||
let busName = keyfile.get_string(group, 'BusName');
|
||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||
|
||||
if (data.objectPaths[objectPath])
|
||||
return;
|
||||
|
||||
let appInfo = null;
|
||||
try {
|
||||
let desktopId = keyfile.get_string(group, 'DesktopId');
|
||||
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
||||
} catch (e) {
|
||||
log('Ignoring search provider ' + path + ': missing DesktopId');
|
||||
keyfile.load_from_file(path, 0);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
let version = '1';
|
||||
if (!keyfile.has_group(KEY_FILE_GROUP))
|
||||
return;
|
||||
|
||||
let remoteProvider;
|
||||
try {
|
||||
version = keyfile.get_string(group, 'Version');
|
||||
} catch (e) {
|
||||
// ignore error
|
||||
let group = KEY_FILE_GROUP;
|
||||
let busName = keyfile.get_string(group, 'BusName');
|
||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||
|
||||
if (objectPaths[objectPath])
|
||||
return;
|
||||
|
||||
let appInfo = null;
|
||||
try {
|
||||
let desktopId = keyfile.get_string(group, 'DesktopId');
|
||||
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
||||
} catch (e) {
|
||||
log('Ignoring search provider ' + path + ': missing DesktopId');
|
||||
return;
|
||||
}
|
||||
|
||||
let version = '1';
|
||||
try {
|
||||
version = keyfile.get_string(group, 'Version');
|
||||
} catch (e) {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
if (version >= 2)
|
||||
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
|
||||
else
|
||||
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
||||
|
||||
objectPaths[objectPath] = remoteProvider;
|
||||
loadedProviders.push(remoteProvider);
|
||||
} catch(e) {
|
||||
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
||||
}
|
||||
|
||||
if (version >= 2)
|
||||
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
|
||||
else
|
||||
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
||||
|
||||
data.objectPaths[objectPath] = remoteProvider;
|
||||
data.loadedProviders.push(remoteProvider);
|
||||
} catch(e) {
|
||||
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
function remoteProvidersLoaded(loadState) {
|
||||
let dataDirs = GLib.get_system_data_dirs();
|
||||
dataDirs.forEach(function(dataDir) {
|
||||
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
|
||||
let dir = Gio.File.new_for_path(path);
|
||||
let fileEnum;
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::name,standard::type',
|
||||
Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch (e) {
|
||||
fileEnum = null;
|
||||
}
|
||||
if (fileEnum != null) {
|
||||
let info;
|
||||
while ((info = fileEnum.next_file(null)))
|
||||
loadRemoteSearchProvider(fileEnum.get_child(info));
|
||||
}
|
||||
});
|
||||
|
||||
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
let sortOrder = searchSettings.get_strv('sort-order');
|
||||
|
||||
// Special case gnome-control-center to be always active and always first
|
||||
sortOrder.unshift('gnome-control-center.desktop');
|
||||
|
||||
loadState.loadedProviders.sort(
|
||||
function(providerA, providerB) {
|
||||
let idxA, idxB;
|
||||
let appIdA, appIdB;
|
||||
loadedProviders.sort(function(providerA, providerB) {
|
||||
let idxA, idxB;
|
||||
let appIdA, appIdB;
|
||||
|
||||
appIdA = providerA.appInfo.get_id();
|
||||
appIdB = providerB.appInfo.get_id();
|
||||
appIdA = providerA.appInfo.get_id();
|
||||
appIdB = providerB.appInfo.get_id();
|
||||
|
||||
idxA = sortOrder.indexOf(appIdA);
|
||||
idxB = sortOrder.indexOf(appIdB);
|
||||
idxA = sortOrder.indexOf(appIdA);
|
||||
idxB = sortOrder.indexOf(appIdB);
|
||||
|
||||
// if no provider is found in the order, use alphabetical order
|
||||
if ((idxA == -1) && (idxB == -1)) {
|
||||
let nameA = providerA.appInfo.get_name();
|
||||
let nameB = providerB.appInfo.get_name();
|
||||
// if no provider is found in the order, use alphabetical order
|
||||
if ((idxA == -1) && (idxB == -1)) {
|
||||
let nameA = providerA.appInfo.get_name();
|
||||
let nameB = providerB.appInfo.get_name();
|
||||
|
||||
return GLib.utf8_collate(nameA, nameB);
|
||||
}
|
||||
return GLib.utf8_collate(nameA, nameB);
|
||||
}
|
||||
|
||||
// if providerA isn't found, it's sorted after providerB
|
||||
if (idxA == -1)
|
||||
return 1;
|
||||
// if providerA isn't found, it's sorted after providerB
|
||||
if (idxA == -1)
|
||||
return 1;
|
||||
|
||||
// if providerB isn't found, it's sorted after providerA
|
||||
if (idxB == -1)
|
||||
return -1;
|
||||
// if providerB isn't found, it's sorted after providerA
|
||||
if (idxB == -1)
|
||||
return -1;
|
||||
|
||||
// finally, if both providers are found, return their order in the list
|
||||
return (idxA - idxB);
|
||||
});
|
||||
// finally, if both providers are found, return their order in the list
|
||||
return (idxA - idxB);
|
||||
});
|
||||
|
||||
loadState.loadedProviders.forEach(
|
||||
function(provider) {
|
||||
loadState.addProviderCallback(provider);
|
||||
});
|
||||
loadedProviders.forEach(addProviderCallback);
|
||||
}
|
||||
|
||||
const RemoteSearchProvider = new Lang.Class({
|
||||
|
@ -23,6 +23,7 @@ const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const SmartcardManager = imports.misc.smartcardManager;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
@ -516,6 +517,13 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
||||
|
||||
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||
this._smartcardManager.connect('smartcard-inserted',
|
||||
Lang.bind(this, function() {
|
||||
if (this._isLocked)
|
||||
this._liftShield(true, 0);
|
||||
}));
|
||||
|
||||
this._inhibitor = null;
|
||||
this._aboutToSuspend = false;
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
@ -720,6 +728,8 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
|
||||
if (this._lockScreenState != MessageTray.State.HIDING)
|
||||
return;
|
||||
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
|
||||
// Complete motion automatically
|
||||
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
|
||||
@ -762,19 +772,6 @@ const ScreenShield = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._becomeModal()) {
|
||||
// We could not become modal, so we can't activate the
|
||||
// screenshield. The user is probably very upset at this
|
||||
// point, but any application using global grabs is broken
|
||||
// Just tell him to stop using this app
|
||||
//
|
||||
// XXX: another option is to kick the user into the gdm login
|
||||
// screen, where we're not affected by grabs
|
||||
Main.notifyError(_("Unable to lock"),
|
||||
_("Lock was blocked by an application"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._lightbox.actor.visible ||
|
||||
this._isActive) {
|
||||
// We're either shown and active, or in the process of
|
||||
@ -788,6 +785,19 @@ const ScreenShield = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._becomeModal()) {
|
||||
// We could not become modal, so we can't activate the
|
||||
// screenshield. The user is probably very upset at this
|
||||
// point, but any application using global grabs is broken
|
||||
// Just tell him to stop using this app
|
||||
//
|
||||
// XXX: another option is to kick the user into the gdm login
|
||||
// screen, where we're not affected by grabs
|
||||
Main.notifyError(_("Unable to lock"),
|
||||
_("Lock was blocked by an application"));
|
||||
return;
|
||||
}
|
||||
|
||||
this._lightbox.show();
|
||||
|
||||
if (this._activationTime == 0)
|
||||
@ -925,7 +935,6 @@ const ScreenShield = new Lang.Class({
|
||||
}
|
||||
|
||||
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
||||
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
|
||||
}
|
||||
|
||||
this._dialog.allowCancel = allowCancel;
|
||||
@ -935,10 +944,6 @@ const ScreenShield = new Lang.Class({
|
||||
this._resetLockScreen(true, false);
|
||||
},
|
||||
|
||||
_onUnlockSucceded: function() {
|
||||
this.deactivate(true);
|
||||
},
|
||||
|
||||
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
||||
// Don't reset the lock screen unless it is completely hidden
|
||||
// This prevents the shield going down if the lock-delay timeout
|
||||
@ -1124,6 +1129,12 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
deactivate: function(animate) {
|
||||
this._dialog.finish(Lang.bind(this, function() {
|
||||
this._finishDeactivate(animate);
|
||||
}));
|
||||
},
|
||||
|
||||
_finishDeactivate: function(animate) {
|
||||
this._hideLockScreen(animate, 0);
|
||||
|
||||
if (this._hasLockScreen)
|
||||
|
@ -31,7 +31,7 @@ const SearchSystem = new Lang.Class({
|
||||
|
||||
let remoteIndex = this._remoteProviders.indexOf(provider);
|
||||
if (remoteIndex != -1)
|
||||
this._remoteProviders.splice(index, 1);
|
||||
this._remoteProviders.splice(remoteIndex, 1);
|
||||
},
|
||||
|
||||
getProviders: function() {
|
||||
|
@ -60,7 +60,7 @@ const _modes = {
|
||||
unlockDialog: undefined,
|
||||
components: ['polkitAgent', 'telepathyClient'],
|
||||
panel: {
|
||||
left: ['userMenu'],
|
||||
left: [],
|
||||
center: [],
|
||||
right: ['lockScreen']
|
||||
},
|
||||
@ -72,7 +72,7 @@ const _modes = {
|
||||
unlockDialog: undefined,
|
||||
components: ['polkitAgent', 'telepathyClient'],
|
||||
panel: {
|
||||
left: ['userMenu'],
|
||||
left: [],
|
||||
center: [],
|
||||
right: ['a11y', 'keyboard', 'lockScreen']
|
||||
},
|
||||
@ -97,7 +97,7 @@ const _modes = {
|
||||
left: ['activities', 'appMenu'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
|
||||
'network', 'battery', 'userMenu']
|
||||
'network', 'battery', 'system']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -41,6 +41,7 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<signal name="AcceleratorActivated">
|
||||
<arg name="action" type="u" />
|
||||
<arg name="deviceid" type="u" />
|
||||
<arg name="timestamp" type="u" />
|
||||
</signal>
|
||||
<property name="Mode" type="s" access="read" />
|
||||
<property name="OverviewActive" type="b" access="readwrite" />
|
||||
@ -79,8 +80,8 @@ const GnomeShell = new Lang.Class({
|
||||
this._grabbers = new Hash.Map();
|
||||
|
||||
global.display.connect('accelerator-activated', Lang.bind(this,
|
||||
function(display, action, deviceid) {
|
||||
this._emitAcceleratorActivated(action, deviceid);
|
||||
function(display, action, deviceid, timestamp) {
|
||||
this._emitAcceleratorActivated(action, deviceid, timestamp);
|
||||
}));
|
||||
},
|
||||
|
||||
@ -166,7 +167,7 @@ const GnomeShell = new Lang.Class({
|
||||
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
|
||||
},
|
||||
|
||||
_emitAcceleratorActivated: function(action, deviceid) {
|
||||
_emitAcceleratorActivated: function(action, deviceid, timestamp) {
|
||||
let destination = this._grabbedAccelerators.get(action);
|
||||
if (!destination)
|
||||
return;
|
||||
@ -177,7 +178,7 @@ const GnomeShell = new Lang.Class({
|
||||
this._dbusImpl.get_object_path(),
|
||||
info ? info.name : null,
|
||||
'AcceleratorActivated',
|
||||
GLib.Variant.new('(uu)', [action, deviceid]));
|
||||
GLib.Variant.new('(uuu)', [action, deviceid, timestamp]));
|
||||
},
|
||||
|
||||
_grabAcceleratorForSender: function(accelerator, flags, sender) {
|
||||
|
@ -48,10 +48,10 @@ const Slider = new Lang.Class({
|
||||
let [hasHandleColor, handleBorderColor] =
|
||||
themeNode.lookup_color('-slider-handle-border-color', false);
|
||||
|
||||
let sliderWidth = width - 2 * handleRadius;
|
||||
let sliderHeight = themeNode.get_length('-slider-height');
|
||||
|
||||
let sliderBorderWidth = themeNode.get_length('-slider-border-width');
|
||||
let sliderBorderRadius = Math.min(width, sliderHeight) / 2;
|
||||
|
||||
let sliderBorderColor = themeNode.get_color('-slider-border-color');
|
||||
let sliderColor = themeNode.get_color('-slider-background-color');
|
||||
@ -59,75 +59,63 @@ const Slider = new Lang.Class({
|
||||
let sliderActiveBorderColor = themeNode.get_color('-slider-active-border-color');
|
||||
let sliderActiveColor = themeNode.get_color('-slider-active-background-color');
|
||||
|
||||
cr.setSourceRGBA (
|
||||
sliderActiveColor.red / 255,
|
||||
sliderActiveColor.green / 255,
|
||||
sliderActiveColor.blue / 255,
|
||||
sliderActiveColor.alpha / 255);
|
||||
cr.rectangle(handleRadius, (height - sliderHeight) / 2, sliderWidth * this._value, sliderHeight);
|
||||
const TAU = Math.PI * 2;
|
||||
|
||||
let handleX = handleRadius + (width - 2 * handleRadius) * this._value;
|
||||
|
||||
cr.arc(sliderBorderRadius + sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 1/4, TAU * 3/4);
|
||||
cr.lineTo(handleX, (height - sliderHeight) / 2);
|
||||
cr.lineTo(handleX, (height + sliderHeight) / 2);
|
||||
cr.lineTo(sliderBorderRadius + sliderBorderWidth, (height + sliderHeight) / 2);
|
||||
Clutter.cairo_set_source_color(cr, sliderActiveColor);
|
||||
cr.fillPreserve();
|
||||
cr.setSourceRGBA (
|
||||
sliderActiveBorderColor.red / 255,
|
||||
sliderActiveBorderColor.green / 255,
|
||||
sliderActiveBorderColor.blue / 255,
|
||||
sliderActiveBorderColor.alpha / 255);
|
||||
Clutter.cairo_set_source_color(cr, sliderActiveBorderColor);
|
||||
cr.setLineWidth(sliderBorderWidth);
|
||||
cr.stroke();
|
||||
|
||||
cr.setSourceRGBA (
|
||||
sliderColor.red / 255,
|
||||
sliderColor.green / 255,
|
||||
sliderColor.blue / 255,
|
||||
sliderColor.alpha / 255);
|
||||
cr.rectangle(handleRadius + sliderWidth * this._value, (height - sliderHeight) / 2, sliderWidth * (1 - this._value), sliderHeight);
|
||||
cr.arc(width - sliderBorderRadius - sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 3/4, TAU * 1/4);
|
||||
cr.lineTo(handleX, (height + sliderHeight) / 2);
|
||||
cr.lineTo(handleX, (height - sliderHeight) / 2);
|
||||
cr.lineTo(width - sliderBorderRadius - sliderBorderWidth, (height - sliderHeight) / 2);
|
||||
Clutter.cairo_set_source_color(cr, sliderColor);
|
||||
cr.fillPreserve();
|
||||
cr.setSourceRGBA (
|
||||
sliderBorderColor.red / 255,
|
||||
sliderBorderColor.green / 255,
|
||||
sliderBorderColor.blue / 255,
|
||||
sliderBorderColor.alpha / 255);
|
||||
Clutter.cairo_set_source_color(cr, sliderBorderColor);
|
||||
cr.setLineWidth(sliderBorderWidth);
|
||||
cr.stroke();
|
||||
|
||||
let handleY = height / 2;
|
||||
let handleX = handleRadius + (width - 2 * handleRadius) * this._value;
|
||||
|
||||
let color = themeNode.get_foreground_color();
|
||||
cr.setSourceRGBA (
|
||||
color.red / 255,
|
||||
color.green / 255,
|
||||
color.blue / 255,
|
||||
color.alpha / 255);
|
||||
Clutter.cairo_set_source_color(cr, color);
|
||||
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
|
||||
cr.fillPreserve();
|
||||
if (hasHandleColor && handleBorderWidth) {
|
||||
cr.setSourceRGBA(
|
||||
handleBorderColor.red / 255,
|
||||
handleBorderColor.green / 255,
|
||||
handleBorderColor.blue / 255,
|
||||
handleBorderColor.alpha / 255);
|
||||
cr.setLineWidth(handleBorderWidth);
|
||||
cr.stroke();
|
||||
Clutter.cairo_set_source_color(cr, handleBorderColor);
|
||||
cr.setLineWidth(handleBorderWidth);
|
||||
cr.stroke();
|
||||
}
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_startDragging: function(actor, event) {
|
||||
if (this._dragging) // don't allow two drags at the same time
|
||||
this.startDragging(event);
|
||||
},
|
||||
|
||||
startDragging: function(event) {
|
||||
if (this._dragging)
|
||||
return false;
|
||||
|
||||
this._dragging = true;
|
||||
|
||||
// FIXME: we should only grab the specific device that originated
|
||||
// the event, but for some weird reason events are still delivered
|
||||
// outside the slider if using clutter_grab_pointer_for_device
|
||||
Clutter.grab_pointer(this.actor);
|
||||
let device = event.get_device();
|
||||
device.grab(this.actor);
|
||||
this._grabbedDevice = device;
|
||||
|
||||
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
|
||||
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
|
||||
let absX, absY;
|
||||
[absX, absY] = event.get_coords();
|
||||
this._moveHandle(absX, absY);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -136,7 +124,8 @@ const Slider = new Lang.Class({
|
||||
this.actor.disconnect(this._releaseId);
|
||||
this.actor.disconnect(this._motionId);
|
||||
|
||||
Clutter.ungrab_pointer();
|
||||
this._grabbedDevice.ungrab();
|
||||
this._grabbedDevice = null;
|
||||
this._dragging = false;
|
||||
|
||||
this.emit('drag-end');
|
||||
|
@ -68,9 +68,6 @@ const ATIndicator = new Lang.Class({
|
||||
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
|
||||
this.menu.addMenuItem(mouseKeys);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
|
||||
|
||||
this._syncMenuVisibility();
|
||||
},
|
||||
|
||||
|
@ -13,13 +13,6 @@ const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const ConnectionState = {
|
||||
DISCONNECTED: 0,
|
||||
CONNECTED: 1,
|
||||
DISCONNECTING: 2,
|
||||
CONNECTING: 3
|
||||
}
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'BTIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
@ -27,61 +20,18 @@ const Indicator = new Lang.Class({
|
||||
_init: function() {
|
||||
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
|
||||
|
||||
// The Bluetooth menu only appears when Bluetooth is in use,
|
||||
// so just statically build it with a "Turn Off" menu item.
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
|
||||
this._item.icon.icon_name = 'bluetooth-active-symbolic';
|
||||
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
|
||||
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
||||
}));
|
||||
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
||||
|
||||
this._applet = new GnomeBluetoothApplet.Applet();
|
||||
|
||||
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
|
||||
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
|
||||
this._killswitch.connect('toggled', Lang.bind(this, function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
|
||||
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
|
||||
this._applet.killswitch_state = this._killswitch.state ?
|
||||
GnomeBluetooth.KillswitchState.UNBLOCKED:
|
||||
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
||||
} else
|
||||
this._killswitch.setToggleState(false);
|
||||
}));
|
||||
|
||||
this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
|
||||
this._applet.connect('notify::discoverable', Lang.bind(this, function() {
|
||||
this._discoverable.setToggleState(this._applet.discoverable);
|
||||
}));
|
||||
this._discoverable.connect('toggled', Lang.bind(this, function() {
|
||||
this._applet.discoverable = this._discoverable.state;
|
||||
}));
|
||||
|
||||
this._updateKillswitch();
|
||||
this.menu.addMenuItem(this._killswitch);
|
||||
this.menu.addMenuItem(this._discoverable);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
||||
new PopupMenu.PopupMenuItem(_("Send Files to Device…")),
|
||||
new PopupMenu.PopupMenuItem(_("Set Up a New Device…")),
|
||||
new PopupMenu.PopupSeparatorMenuItem()];
|
||||
this._hasDevices = false;
|
||||
|
||||
this._fullMenuItems[1].connect('activate', function() {
|
||||
GLib.spawn_command_line_async('bluetooth-sendto');
|
||||
});
|
||||
this._fullMenuItems[2].connect('activate', function() {
|
||||
GLib.spawn_command_line_async('bluetooth-wizard');
|
||||
});
|
||||
|
||||
for (let i = 0; i < this._fullMenuItems.length; i++) {
|
||||
let item = this._fullMenuItems[i];
|
||||
this.menu.addMenuItem(item);
|
||||
}
|
||||
|
||||
this._deviceItemPosition = 3;
|
||||
this._deviceItems = [];
|
||||
this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
|
||||
this._updateDevices();
|
||||
|
||||
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
||||
this._updateFullMenu();
|
||||
|
||||
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
||||
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
|
||||
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
||||
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
||||
@ -90,199 +40,18 @@ const Indicator = new Lang.Class({
|
||||
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
_updateKillswitch: function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
|
||||
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
|
||||
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
|
||||
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
|
||||
_sync: function() {
|
||||
let connectedDevices = this._applet.get_devices().filter(function(device) {
|
||||
return device.connected;
|
||||
});
|
||||
let nDevices = connectedDevices.length;
|
||||
|
||||
this._killswitch.setToggleState(on);
|
||||
if (can_toggle)
|
||||
this._killswitch.setStatus(null);
|
||||
else
|
||||
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
|
||||
this._killswitch.setStatus(_("hardware disabled"));
|
||||
let on = nDevices > 0;
|
||||
this.mainIcon.visible = on;
|
||||
this.actor.visible = on;
|
||||
|
||||
this.actor.visible = has_adapter;
|
||||
|
||||
if (on) {
|
||||
this._discoverable.actor.show();
|
||||
this.setIcon('bluetooth-active-symbolic');
|
||||
} else {
|
||||
this._discoverable.actor.hide();
|
||||
this.setIcon('bluetooth-disabled-symbolic');
|
||||
}
|
||||
},
|
||||
|
||||
_updateDevices: function() {
|
||||
let devices = this._applet.get_devices();
|
||||
|
||||
let newlist = [ ];
|
||||
for (let i = 0; i < this._deviceItems.length; i++) {
|
||||
let item = this._deviceItems[i];
|
||||
let destroy = true;
|
||||
for (let j = 0; j < devices.length; j++) {
|
||||
if (item._device.device_path == devices[j].device_path) {
|
||||
this._updateDeviceItem(item, devices[j]);
|
||||
destroy = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (destroy)
|
||||
item.destroy();
|
||||
else
|
||||
newlist.push(item);
|
||||
}
|
||||
|
||||
this._deviceItems = newlist;
|
||||
this._hasDevices = newlist.length > 0;
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
let d = devices[i];
|
||||
if (d._item)
|
||||
continue;
|
||||
let item = this._createDeviceItem(d);
|
||||
if (item) {
|
||||
this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
|
||||
this._deviceItems.push(item);
|
||||
this._hasDevices = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateDeviceItem: function(item, device) {
|
||||
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) {
|
||||
item.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
let prevDevice = item._device;
|
||||
let prevCapabilities = prevDevice.capabilities;
|
||||
let prevCanConnect = prevDevice.can_connect;
|
||||
|
||||
// adopt the new device object
|
||||
item._device = device;
|
||||
device._item = item;
|
||||
|
||||
// update properties
|
||||
item.label.text = device.alias;
|
||||
|
||||
if (prevCapabilities != device.capabilities ||
|
||||
prevCanConnect != device.can_connect) {
|
||||
// need to rebuild the submenu
|
||||
item.menu.removeAll();
|
||||
this._buildDeviceSubMenu(item, device);
|
||||
}
|
||||
|
||||
// update connected property
|
||||
if (device.can_connect)
|
||||
item._connectedMenuItem.setToggleState(device.connected);
|
||||
},
|
||||
|
||||
_createDeviceItem: function(device) {
|
||||
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
|
||||
return null;
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
|
||||
|
||||
// adopt the device object, and add a back link
|
||||
item._device = device;
|
||||
device._item = item;
|
||||
|
||||
this._buildDeviceSubMenu(item, device);
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
_buildDeviceSubMenu: function(item, device) {
|
||||
if (device.can_connect) {
|
||||
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
|
||||
item._connected = device.connected;
|
||||
item._connectedMenuItem = menuitem;
|
||||
menuitem.connect('toggled', Lang.bind(this, function() {
|
||||
if (item._connected > ConnectionState.CONNECTED) {
|
||||
// operation already in progress, revert
|
||||
// (should not happen anyway)
|
||||
menuitem.setToggleState(menuitem.state);
|
||||
}
|
||||
if (item._connected) {
|
||||
item._connected = ConnectionState.DISCONNECTING;
|
||||
menuitem.setStatus(_("disconnecting..."));
|
||||
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
|
||||
if (success) { // apply
|
||||
item._connected = ConnectionState.DISCONNECTED;
|
||||
menuitem.setToggleState(false);
|
||||
} else { // revert
|
||||
item._connected = ConnectionState.CONNECTED;
|
||||
menuitem.setToggleState(true);
|
||||
}
|
||||
menuitem.setStatus(null);
|
||||
});
|
||||
} else {
|
||||
item._connected = ConnectionState.CONNECTING;
|
||||
menuitem.setStatus(_("connecting..."));
|
||||
this._applet.connect_device(item._device.device_path, function(applet, success) {
|
||||
if (success) { // apply
|
||||
item._connected = ConnectionState.CONNECTED;
|
||||
menuitem.setToggleState(true);
|
||||
} else { // revert
|
||||
item._connected = ConnectionState.DISCONNECTED;
|
||||
menuitem.setToggleState(false);
|
||||
}
|
||||
menuitem.setStatus(null);
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
item.menu.addMenuItem(menuitem);
|
||||
}
|
||||
|
||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
|
||||
item.menu.addAction(_("Send Files…"), Lang.bind(this, function() {
|
||||
this._applet.send_to_address(device.bdaddr, device.alias);
|
||||
}));
|
||||
}
|
||||
|
||||
switch (device.type) {
|
||||
case GnomeBluetoothApplet.Type.KEYBOARD:
|
||||
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.MOUSE:
|
||||
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.HEADSET:
|
||||
case GnomeBluetoothApplet.Type.HEADPHONES:
|
||||
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
|
||||
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_updateFullMenu: function() {
|
||||
if (this._applet.show_full_menu) {
|
||||
this._showAll(this._fullMenuItems);
|
||||
if (this._hasDevices)
|
||||
this._showAll(this._deviceItems);
|
||||
} else {
|
||||
this._hideAll(this._fullMenuItems);
|
||||
this._hideAll(this._deviceItems);
|
||||
}
|
||||
},
|
||||
|
||||
_showAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].actor.show();
|
||||
},
|
||||
|
||||
_hideAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].actor.hide();
|
||||
},
|
||||
|
||||
_destroyAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].destroy();
|
||||
if (on)
|
||||
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
|
@ -398,8 +398,6 @@ const InputSourceIndicator = new Lang.Class({
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
|
||||
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
|
||||
|
||||
this._sourcesPerWindow = false;
|
||||
this._focusWindowNotifyId = 0;
|
||||
this._overviewShowingId = 0;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,39 +2,15 @@
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const UPower = imports.gi.UPowerGlib;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||
|
||||
const UPDeviceType = {
|
||||
UNKNOWN: 0,
|
||||
AC_POWER: 1,
|
||||
BATTERY: 2,
|
||||
UPS: 3,
|
||||
MONITOR: 4,
|
||||
MOUSE: 5,
|
||||
KEYBOARD: 6,
|
||||
PDA: 7,
|
||||
PHONE: 8,
|
||||
MEDIA_PLAYER: 9,
|
||||
TABLET: 10,
|
||||
COMPUTER: 11
|
||||
};
|
||||
|
||||
const UPDeviceState = {
|
||||
UNKNOWN: 0,
|
||||
CHARGING: 1,
|
||||
DISCHARGING: 2,
|
||||
EMPTY: 3,
|
||||
FULLY_CHARGED: 4,
|
||||
PENDING_CHARGE: 5,
|
||||
PENDING_DISCHARGE: 6
|
||||
};
|
||||
|
||||
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
|
||||
<method name="GetDevices">
|
||||
<arg type="a(susdut)" direction="out" />
|
||||
@ -55,96 +31,73 @@ const Indicator = new Lang.Class({
|
||||
this.parent('battery-missing-symbolic', _("Battery"));
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._devicesChanged));
|
||||
this._devicesChanged();
|
||||
}));
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
}));
|
||||
|
||||
this._deviceItems = [ ];
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true);
|
||||
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
|
||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||
this.menu.addMenuItem(this._batteryItem);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._otherDevicePosition = 2;
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_readPrimaryDevice: function() {
|
||||
_sessionUpdated: function() {
|
||||
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
this.menu.setSensitive(sensitive);
|
||||
},
|
||||
|
||||
_statusForDevice: function(device) {
|
||||
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
||||
|
||||
if (state == UPower.DeviceState.FULLY_CHARGED)
|
||||
return _("Fully Charged");
|
||||
|
||||
let time = Math.round(seconds / 60);
|
||||
if (time == 0) {
|
||||
// 0 is reported when UPower does not have enough data
|
||||
// to estimate battery life
|
||||
return _("Estimating…");
|
||||
}
|
||||
|
||||
let minutes = time % 60;
|
||||
let hours = Math.floor(time / 60);
|
||||
|
||||
if (state == UPower.DeviceState.DISCHARGING) {
|
||||
// Translators: this is <hours>:<minutes> Remaining (<percentage>)
|
||||
return _("%d\u2236%d Remaining (%d%%)".format(hours, minutes, percentage));
|
||||
}
|
||||
|
||||
if (state == UPower.DeviceState.CHARGING) {
|
||||
// Translators: this is <hours>:<minutes> Until Full (<percentage>)
|
||||
return _("%d\u2236%d Until Full (%d%%)".format(hours, minutes, percentage));
|
||||
}
|
||||
|
||||
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
|
||||
return _("Estimating…");
|
||||
},
|
||||
|
||||
_syncStatusLabel: function() {
|
||||
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
|
||||
if (error) {
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
this._batteryItem.actor.hide();
|
||||
this._item.actor.hide();
|
||||
return;
|
||||
}
|
||||
let [[device_id, device_type, icon, percentage, state, seconds]] = result;
|
||||
if (device_type == UPDeviceType.BATTERY) {
|
||||
this._hasPrimary = true;
|
||||
let time = Math.round(seconds / 60);
|
||||
if (time == 0) {
|
||||
// 0 is reported when UPower does not have enough data
|
||||
// to estimate battery life
|
||||
this._batteryItem.label.text = _("Estimating…");
|
||||
} else {
|
||||
let minutes = time % 60;
|
||||
let hours = Math.floor(time / 60);
|
||||
let timestring;
|
||||
if (time >= 60) {
|
||||
if (minutes == 0) {
|
||||
timestring = ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
|
||||
} else {
|
||||
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
|
||||
let template = _("%d %s %d %s remaining");
|
||||
|
||||
timestring = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
|
||||
}
|
||||
} else
|
||||
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
||||
this._batteryItem.label.text = timestring;
|
||||
}
|
||||
this._primaryPercentage.text = C_("percent of battery remaining", "%d%%").format(Math.round(percentage));
|
||||
this._batteryItem.actor.show();
|
||||
let [device] = result;
|
||||
let [device_id, device_type] = device;
|
||||
if (device_type == UPower.DeviceKind.BATTERY) {
|
||||
this._item.status.text = this._statusForDevice(device);
|
||||
this._item.actor.show();
|
||||
} else {
|
||||
this._hasPrimary = false;
|
||||
this._batteryItem.actor.hide();
|
||||
}
|
||||
|
||||
this._primaryDeviceId = device_id;
|
||||
}));
|
||||
},
|
||||
|
||||
_readOtherDevices: function() {
|
||||
this._proxy.GetDevicesRemote(Lang.bind(this, function(result, error) {
|
||||
this._deviceItems.forEach(function(i) { i.destroy(); });
|
||||
this._deviceItems = [];
|
||||
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
let position = 0;
|
||||
let [devices] = result;
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
let [device_id, device_type] = devices[i];
|
||||
if (device_type == UPDeviceType.AC_POWER || device_id == this._primaryDeviceId)
|
||||
continue;
|
||||
|
||||
let item = new DeviceItem (devices[i]);
|
||||
this._deviceItems.push(item);
|
||||
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
||||
position++;
|
||||
this._item.actor.hide();
|
||||
}
|
||||
}));
|
||||
},
|
||||
@ -156,71 +109,15 @@ const Indicator = new Lang.Class({
|
||||
if (icon) {
|
||||
let gicon = Gio.icon_new_for_string(icon);
|
||||
this.setGIcon(gicon);
|
||||
this._item.icon.gicon = gicon;
|
||||
hasIcon = true;
|
||||
}
|
||||
this.mainIcon.visible = hasIcon;
|
||||
this.actor.visible = hasIcon;
|
||||
},
|
||||
|
||||
_devicesChanged: function() {
|
||||
_sync: function() {
|
||||
this._syncIcon();
|
||||
this._readPrimaryDevice();
|
||||
this._readOtherDevices();
|
||||
}
|
||||
});
|
||||
|
||||
const DeviceItem = new Lang.Class({
|
||||
Name: 'DeviceItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function(device) {
|
||||
this.parent({ reactive: false });
|
||||
|
||||
let [device_id, device_type, icon, percentage, state, time] = device;
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
|
||||
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
|
||||
|
||||
this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon),
|
||||
style_class: 'popup-menu-icon' });
|
||||
|
||||
this._box.add_actor(this._icon);
|
||||
this._box.add_actor(this._label);
|
||||
this.addActor(this._box);
|
||||
|
||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
|
||||
style_class: 'popup-battery-percentage' });
|
||||
this.addActor(percentLabel, { align: St.Align.END });
|
||||
//FIXME: ideally we would like to expose this._label and percentLabel
|
||||
this.actor.label_actor = percentLabel;
|
||||
},
|
||||
|
||||
_deviceTypeToString: function(type) {
|
||||
switch (type) {
|
||||
case UPDeviceType.AC_POWER:
|
||||
return _("AC Adapter");
|
||||
case UPDeviceType.BATTERY:
|
||||
return _("Laptop Battery");
|
||||
case UPDeviceType.UPS:
|
||||
return _("UPS");
|
||||
case UPDeviceType.MONITOR:
|
||||
return _("Monitor");
|
||||
case UPDeviceType.MOUSE:
|
||||
return _("Mouse");
|
||||
case UPDeviceType.KEYBOARD:
|
||||
return _("Keyboard");
|
||||
case UPDeviceType.PDA:
|
||||
return _("PDA");
|
||||
case UPDeviceType.PHONE:
|
||||
return _("Cell Phone");
|
||||
case UPDeviceType.MEDIA_PLAYER:
|
||||
return _("Media Player");
|
||||
case UPDeviceType.TABLET:
|
||||
return _("Tablet");
|
||||
case UPDeviceType.COMPUTER:
|
||||
return _("Computer");
|
||||
default:
|
||||
return C_("device", "Unknown");
|
||||
}
|
||||
this._syncStatusLabel();
|
||||
}
|
||||
});
|
||||
|
358
js/ui/status/system.js
Normal file
358
js/ui/status/system.js
Normal file
@ -0,0 +1,358 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
|
||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<property name="Remote" type="b" access="read"/>
|
||||
<property name="Class" type="s" access="read"/>
|
||||
<property name="Type" type="s" access="read"/>
|
||||
<property name="State" type="s" access="read"/>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'SystemIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('system-shutdown-symbolic', _("System"));
|
||||
|
||||
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
|
||||
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._haveShutdown = true;
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
|
||||
this._createSubMenu();
|
||||
|
||||
this._userManager.connect('notify::is-loaded',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('notify::has-multiple-users',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('user-added',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('user-removed',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||
Lang.bind(this, this._updateLockScreen));
|
||||
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._updateSwitchUser();
|
||||
this._updateMultiUser();
|
||||
|
||||
// Whether shutdown is available or not depends on both lockdown
|
||||
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
||||
// notify, so we update the menu item each time the menu opens or
|
||||
// the lockdown setting changes, which should be close enough.
|
||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||
function(menu, open) {
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
this._updateHaveShutdown();
|
||||
}));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this._updateLockScreen();
|
||||
this._updatePowerOff();
|
||||
this._settingsAction.visible = Main.sessionMode.allowSettings;
|
||||
},
|
||||
|
||||
_updateMultiUser: function() {
|
||||
let shouldShowInMode = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
let hasSwitchUser = this._updateSwitchUser();
|
||||
let hasLogout = this._updateLogout();
|
||||
|
||||
this._switchUserSubMenu.actor.visible = shouldShowInMode && (hasSwitchUser || hasLogout);
|
||||
},
|
||||
|
||||
_updateSwitchUser: function() {
|
||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
|
||||
|
||||
let visible = allowSwitch && multiUser;
|
||||
this._loginScreenItem.actor.visible = visible;
|
||||
return visible;
|
||||
},
|
||||
|
||||
_updateLogout: function() {
|
||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
|
||||
let systemAccount = this._user.system_account;
|
||||
let localAccount = this._user.local_account;
|
||||
let multiUser = this._userManager.has_multiple_users;
|
||||
let multiSession = Gdm.get_session_ids().length > 1;
|
||||
|
||||
let visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
|
||||
this._logoutItem.actor.visible = visible;
|
||||
return visible;
|
||||
},
|
||||
|
||||
_updateSwitchUserSubMenu: function() {
|
||||
this._switchUserSubMenu.label.text = this._user.get_real_name();
|
||||
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
iconFile = null;
|
||||
|
||||
if (iconFile) {
|
||||
let file = Gio.File.new_for_path(iconFile);
|
||||
let gicon = new Gio.FileIcon({ file: file });
|
||||
this._switchUserSubMenu.icon.gicon = gicon;
|
||||
} else {
|
||||
this._switchUserSubMenu.icon_name = 'avatar-default-symbolic';
|
||||
}
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let showLock = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
this._lockScreenAction.visible = showLock && allowLockScreen && LoginManager.canLock();
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._session.CanShutdownRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._haveShutdown = result[0];
|
||||
this._updatePowerOff();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_updatePowerOff: function() {
|
||||
this._powerOffAction.visible = this._haveShutdown && !Main.sessionMode.isLocked;
|
||||
},
|
||||
|
||||
_createActionButton: function(iconName, accessibleName) {
|
||||
let icon = new St.Button({ reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
accessible_name: accessibleName,
|
||||
style_class: 'system-menu-action' });
|
||||
icon.child = new St.Icon({ icon_name: iconName });
|
||||
return icon;
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
this._switchUserSubMenu = new PopupMenu.PopupSubMenuMenuItem('', true);
|
||||
this._switchUserSubMenu.icon.style_class = 'system-switch-user-submenu-icon';
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||
this._switchUserSubMenu.menu.addMenuItem(item);
|
||||
this._loginScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Log Out"));
|
||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||
this._switchUserSubMenu.menu.addMenuItem(item);
|
||||
this._logoutItem = item;
|
||||
|
||||
this._user.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUserSubMenu));
|
||||
this._user.connect('changed', Lang.bind(this, this._updateSwitchUserSubMenu));
|
||||
|
||||
this.menu.addMenuItem(this._switchUserSubMenu);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
let hbox = new St.BoxLayout({ style_class: 'system-menu-actions-box' });
|
||||
|
||||
this._settingsAction = this._createActionButton('preferences-system-symbolic', _("Settings"));
|
||||
this._settingsAction.connect('clicked', Lang.bind(this, this._onSettingsClicked));
|
||||
hbox.add(this._settingsAction, { expand: true, x_fill: false });
|
||||
|
||||
this._lockScreenAction = this._createActionButton('changes-prevent-symbolic', _("Lock"));
|
||||
this._lockScreenAction.connect('clicked', Lang.bind(this, this._onLockScreenClicked));
|
||||
hbox.add(this._lockScreenAction, { expand: true, x_fill: false });
|
||||
|
||||
this._powerOffAction = this._createActionButton('system-shutdown-symbolic', _("Power Off"));
|
||||
this._powerOffAction.connect('clicked', Lang.bind(this, this._onPowerOffClicked));
|
||||
hbox.add(this._powerOffAction, { expand: true, x_fill: false });
|
||||
|
||||
item = new PopupMenu.PopupBaseMenuItem({ reactive: false,
|
||||
can_focus: false });
|
||||
item.addActor(hbox, { expand: true });
|
||||
|
||||
this.menu.addMenuItem(item);
|
||||
},
|
||||
|
||||
_onSettingsClicked: function() {
|
||||
this.menu.itemActivated();
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
|
||||
Main.overview.hide();
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_onLockScreenClicked: function() {
|
||||
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
Main.screenShield.lock(true);
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.lock(false);
|
||||
Gdm.goto_login_session_sync(null);
|
||||
},
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._session.LogoutRemote(0);
|
||||
},
|
||||
|
||||
_openSessionWarnDialog: function(sessions) {
|
||||
let dialog = new ModalDialog.ModalDialog();
|
||||
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
|
||||
text: _("Other users are logged in.") });
|
||||
dialog.contentLayout.add(subjectLabel, { y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
|
||||
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
|
||||
descriptionLabel.clutter_text.line_wrap = true;
|
||||
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
|
||||
y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
|
||||
scrollView.add_style_class_name('vfade');
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
|
||||
|
||||
let userList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(userList);
|
||||
|
||||
for (let i = 0; i < sessions.length; i++) {
|
||||
let session = sessions[i];
|
||||
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
|
||||
vertical: false });
|
||||
let avatar = new UserWidget.Avatar(session.user);
|
||||
avatar.update();
|
||||
userEntry.add(avatar.actor);
|
||||
|
||||
let userLabelText = "";;
|
||||
let userName = session.user.get_real_name() ?
|
||||
session.user.get_real_name() : session.username;
|
||||
|
||||
if (session.info.remote)
|
||||
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||
userLabelText = _("%s (remote)").format(userName);
|
||||
else if (session.info.type == "tty")
|
||||
/* Translators: Console here refers to a tty like a VT console */
|
||||
userLabelText = _("%s (console)").format(userName);
|
||||
else
|
||||
userLabelText = userName;
|
||||
|
||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||
vertical: true });
|
||||
textLayout.add(new St.Label({ text: userLabelText }),
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
expand: true });
|
||||
userEntry.add(textLayout, { expand: true });
|
||||
userList.add(userEntry, { x_fill: true });
|
||||
}
|
||||
|
||||
let cancelButton = { label: _("Cancel"),
|
||||
action: function() { dialog.close(); },
|
||||
key: Clutter.Escape };
|
||||
|
||||
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
|
||||
dialog.close();
|
||||
this._session.ShutdownRemote();
|
||||
}), default: true };
|
||||
|
||||
dialog.setButtons([cancelButton, powerOffButton]);
|
||||
|
||||
dialog.open();
|
||||
},
|
||||
|
||||
_onPowerOffClicked: function() {
|
||||
this.menu.itemActivated();
|
||||
Main.overview.hide();
|
||||
this._loginManager.listSessions(Lang.bind(this, function(result) {
|
||||
let sessions = [];
|
||||
let n = 0;
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let[id, uid, userName, seat, sessionPath] = result[i];
|
||||
let proxy = new SystemdLoginSession(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
sessionPath);
|
||||
|
||||
if (proxy.Class != 'user')
|
||||
continue;
|
||||
|
||||
if (proxy.State == 'closing')
|
||||
continue;
|
||||
|
||||
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
||||
continue;
|
||||
|
||||
sessions.push({ user: this._userManager.get_user(userName),
|
||||
username: userName,
|
||||
info: { type: proxy.Type,
|
||||
remote: proxy.Remote }
|
||||
});
|
||||
|
||||
// limit the number of entries
|
||||
n++;
|
||||
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
||||
break;
|
||||
}
|
||||
|
||||
if (n != 0)
|
||||
this._openSessionWarnDialog(sessions);
|
||||
else
|
||||
this._session.ShutdownRemote();
|
||||
}));
|
||||
}
|
||||
});
|
@ -9,6 +9,7 @@ const Signals = imports.signals;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Slider = imports.ui.slider;
|
||||
|
||||
const VOLUME_NOTIFY_ID = 1;
|
||||
|
||||
@ -28,18 +29,23 @@ function getMixerControl() {
|
||||
const StreamSlider = new Lang.Class({
|
||||
Name: 'StreamSlider',
|
||||
|
||||
_init: function(control, title) {
|
||||
_init: function(control) {
|
||||
this._control = control;
|
||||
|
||||
this.item = new PopupMenu.PopupMenuSection();
|
||||
this.item = new PopupMenu.PopupBaseMenuItem({ activate: false });
|
||||
|
||||
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
|
||||
this._slider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._slider = new Slider.Slider(0);
|
||||
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
|
||||
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
|
||||
this.item.addMenuItem(this._title);
|
||||
this.item.addMenuItem(this._slider);
|
||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
|
||||
this.item.addActor(this._icon, { align: St.Align.MIDDLE });
|
||||
this.item.addActor(this._slider.actor, { expand: true });
|
||||
|
||||
this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
|
||||
this._slider.startDragging(event);
|
||||
}));
|
||||
|
||||
this._stream = null;
|
||||
},
|
||||
@ -83,8 +89,7 @@ const StreamSlider = new Lang.Class({
|
||||
|
||||
_updateVisibility: function() {
|
||||
let visible = this._shouldBeVisible();
|
||||
this._title.actor.visible = visible;
|
||||
this._slider.actor.visible = visible;
|
||||
this.item.actor.visible = visible;
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
@ -178,11 +183,17 @@ const OutputStreamSlider = new Lang.Class({
|
||||
this._portChangedId = 0;
|
||||
},
|
||||
|
||||
_updateSliderIcon: function() {
|
||||
this._icon.icon_name = (this._hasHeadphones ?
|
||||
'audio-headphones-symbolic' :
|
||||
'audio-speakers-symbolic');
|
||||
},
|
||||
|
||||
_portChanged: function() {
|
||||
let hasHeadphones = this._findHeadphones(this._stream);
|
||||
if (hasHeadphones != this._hasHeadphones) {
|
||||
this._hasHeadphones = hasHeadphones;
|
||||
this.emit('headphones-changed', this._hasHeadphones);
|
||||
this._updateSliderIcon();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -191,10 +202,11 @@ const InputStreamSlider = new Lang.Class({
|
||||
Name: 'InputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_init: function(control, title) {
|
||||
this.parent(control, title);
|
||||
_init: function(control) {
|
||||
this.parent(control);
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
this._icon.icon_name = 'audio-input-microphone-symbolic';
|
||||
},
|
||||
|
||||
_connectStream: function(stream) {
|
||||
@ -242,17 +254,13 @@ const VolumeMenu = new Lang.Class({
|
||||
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
|
||||
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
|
||||
|
||||
/* Translators: This is the label for audio volume */
|
||||
this._output = new OutputStreamSlider(this._control, _("Volume"));
|
||||
this._output = new OutputStreamSlider(this._control);
|
||||
this._output.connect('stream-updated', Lang.bind(this, function() {
|
||||
this.emit('icon-changed');
|
||||
}));
|
||||
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
|
||||
this.emit('headphones-changed', value);
|
||||
}));
|
||||
this.addMenuItem(this._output.item);
|
||||
|
||||
this._input = new InputStreamSlider(this._control, _("Microphone"));
|
||||
this._input = new InputStreamSlider(this._control);
|
||||
this.addMenuItem(this._input.item);
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
@ -300,18 +308,9 @@ const Indicator = new Lang.Class({
|
||||
this.actor.visible = (icon != null);
|
||||
this.setIcon(icon);
|
||||
}));
|
||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
|
||||
this._headphoneIcon.visible = value;
|
||||
}));
|
||||
|
||||
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'audio-headphones-symbolic' }));
|
||||
this._headphoneIcon.visible = false;
|
||||
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
||||
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
},
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Atk = imports.gi.Atk;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
@ -12,13 +13,13 @@ const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Panel = imports.ui.panel;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const AuthPrompt = imports.gdm.authPrompt;
|
||||
const Batch = imports.gdm.batch;
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const LoginDialog = imports.gdm.loginDialog;
|
||||
@ -28,91 +29,35 @@ const IDLE_TIMEOUT = 2 * 60;
|
||||
|
||||
const UnlockDialog = new Lang.Class({
|
||||
Name: 'UnlockDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(parentActor) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
|
||||
parentActor: parentActor
|
||||
});
|
||||
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
|
||||
style_class: 'login-dialog',
|
||||
visible: false });
|
||||
|
||||
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
parentActor.add_child(this.actor);
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
this._userName = GLib.get_user_name();
|
||||
this._user = this._userManager.get_user(this._userName);
|
||||
|
||||
this._failCounter = 0;
|
||||
this._firstQuestion = true;
|
||||
this._promptBox = new St.BoxLayout({ vertical: true });
|
||||
this.actor.add_child(this._promptBox);
|
||||
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
|
||||
this._greeterClient = new Gdm.Client();
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
|
||||
this._userVerified = false;
|
||||
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
|
||||
this._authPrompt.connect('failed', Lang.bind(this, this._fail));
|
||||
this._authPrompt.connect('cancelled', Lang.bind(this, this._fail));
|
||||
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._authPrompt.setPasswordChar('\u25cf');
|
||||
this._authPrompt.nextButton.label = _("Unlock");
|
||||
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
|
||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
|
||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
|
||||
|
||||
this._userWidget = new UserWidget.UserWidget(this._user);
|
||||
this.contentLayout.add_actor(this._userWidget.actor);
|
||||
|
||||
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||
vertical: true });
|
||||
|
||||
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||
this._promptLayout.add(this._promptLabel,
|
||||
{ x_align: St.Align.START });
|
||||
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
|
||||
this.setInitialKeyFocus(this._promptEntry);
|
||||
this._promptEntry.clutter_text.connect('text-changed', Lang.bind(this, function() {
|
||||
this._updateOkButtonSensitivity(this._promptEntry.text.length > 0);
|
||||
}));
|
||||
|
||||
this._promptLayout.add(this._promptEntry,
|
||||
{ expand: true,
|
||||
x_fill: true });
|
||||
|
||||
this.contentLayout.add_actor(this._promptLayout);
|
||||
|
||||
this._promptMessage = new St.Label({ visible: false });
|
||||
this.contentLayout.add(this._promptMessage, { x_fill: true });
|
||||
|
||||
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' });
|
||||
this._promptLoginHint.hide();
|
||||
this.contentLayout.add_actor(this._promptLoginHint);
|
||||
this._promptBox.add_child(this._authPrompt.actor);
|
||||
|
||||
this.allowCancel = false;
|
||||
this.buttonLayout.visible = true;
|
||||
this.addButton({ label: _("Cancel"),
|
||||
action: Lang.bind(this, this._escape),
|
||||
key: Clutter.KEY_Escape },
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this.placeSpinner({ expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this._okButton = this.addButton({ label: _("Unlock"),
|
||||
action: Lang.bind(this, this._doUnlock),
|
||||
default: true },
|
||||
{ expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
|
||||
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
|
||||
@ -125,174 +70,100 @@ const UnlockDialog = new Lang.Class({
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
||||
this.dialogLayout.add(this._otherUserButton,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
this._promptBox.add_child(this._otherUserButton);
|
||||
} else {
|
||||
this._otherUserButton = null;
|
||||
}
|
||||
|
||||
this._authPrompt.reset();
|
||||
this._updateSensitivity(true);
|
||||
|
||||
let batch = new Batch.Hold();
|
||||
this._userVerifier.begin(this._userName, batch);
|
||||
|
||||
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Unlock Window"), 'dialog-password-symbolic');
|
||||
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._promptEntry.reactive = sensitive;
|
||||
this._promptEntry.clutter_text.editable = sensitive;
|
||||
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
|
||||
this._authPrompt.updateSensitivity(sensitive);
|
||||
|
||||
if (this._otherUserButton) {
|
||||
this._otherUserButton.reactive = sensitive;
|
||||
this._otherUserButton.can_focus = sensitive;
|
||||
}
|
||||
},
|
||||
|
||||
_updateOkButtonSensitivity: function(sensitive) {
|
||||
this._okButton.reactive = sensitive;
|
||||
this._okButton.can_focus = sensitive;
|
||||
_fail: function() {
|
||||
this.emit('failed');
|
||||
},
|
||||
|
||||
_showMessage: function(userVerifier, message, styleClass) {
|
||||
if (message) {
|
||||
this._promptMessage.text = message;
|
||||
this._promptMessage.styleClass = styleClass;
|
||||
GdmUtil.fadeInActor(this._promptMessage);
|
||||
_onReset: function(authPrompt, beginRequest) {
|
||||
let userName;
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
this._authPrompt.setUser(this._user);
|
||||
userName = this._userName;
|
||||
} else {
|
||||
GdmUtil.fadeOutActor(this._promptMessage);
|
||||
}
|
||||
},
|
||||
|
||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
if (this._firstQuestion && this._firstQuestionAnswer) {
|
||||
this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer);
|
||||
this._firstQuestionAnswer = null;
|
||||
this._firstQuestion = false;
|
||||
return;
|
||||
userName = null;
|
||||
}
|
||||
|
||||
this._promptLabel.text = question;
|
||||
|
||||
if (!this._firstQuestion)
|
||||
this._promptEntry.text = '';
|
||||
else
|
||||
this._firstQuestion = false;
|
||||
|
||||
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
||||
this._promptEntry.menu.isPassword = passwordChar != '';
|
||||
|
||||
this._currentQuery = serviceName;
|
||||
this._updateSensitivity(true);
|
||||
this.setWorking(false);
|
||||
},
|
||||
|
||||
_showLoginHint: function(verifier, message) {
|
||||
this._promptLoginHint.set_text(message)
|
||||
GdmUtil.fadeInActor(this._promptLoginHint);
|
||||
},
|
||||
|
||||
_hideLoginHint: function() {
|
||||
GdmUtil.fadeOutActor(this._promptLoginHint);
|
||||
},
|
||||
|
||||
_doUnlock: function() {
|
||||
if (this._firstQuestion) {
|
||||
// we haven't received a query yet, so stash the answer
|
||||
// and make ourself non-reactive
|
||||
// the actual reply to GDM will be sent as soon as asked
|
||||
this._firstQuestionAnswer = this._promptEntry.text;
|
||||
this._updateSensitivity(false);
|
||||
this.setWorking(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._currentQuery)
|
||||
return;
|
||||
|
||||
let query = this._currentQuery;
|
||||
this._currentQuery = null;
|
||||
|
||||
this._updateSensitivity(false);
|
||||
this.setWorking(true);
|
||||
|
||||
this._userVerifier.answerQuery(query, this._promptEntry.text);
|
||||
},
|
||||
|
||||
_finishUnlock: function() {
|
||||
this._userVerifier.clear();
|
||||
this.emit('unlocked');
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this._userVerified = true;
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._finishUnlock();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._finishUnlock();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
if (!this._userVerified) {
|
||||
this._userVerifier.clear();
|
||||
this.emit('failed');
|
||||
}
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._currentQuery = null;
|
||||
this._firstQuestion = true;
|
||||
this._userVerified = false;
|
||||
|
||||
this._promptEntry.text = '';
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
this._promptEntry.menu.isPassword = true;
|
||||
|
||||
this._updateSensitivity(false);
|
||||
this.setWorking(false);
|
||||
this._authPrompt.begin({ userName: userName });
|
||||
},
|
||||
|
||||
_escape: function() {
|
||||
if (this.allowCancel) {
|
||||
this._userVerifier.cancel();
|
||||
this.emit('failed');
|
||||
}
|
||||
if (this.allowCancel)
|
||||
this._authPrompt.cancel();
|
||||
},
|
||||
|
||||
_otherUserClicked: function(button, event) {
|
||||
Gdm.goto_login_session_sync(null);
|
||||
|
||||
this._userVerifier.cancel();
|
||||
this.emit('failed');
|
||||
this._authPrompt.cancel();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._userVerifier.clear();
|
||||
this.popModal();
|
||||
this.actor.destroy();
|
||||
|
||||
if (this._idleWatchId) {
|
||||
this._idleMonitor.remove_watch(this._idleWatchId);
|
||||
this._idleWatchId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._userVerifier.cancel(null);
|
||||
this._authPrompt.cancel();
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
||||
this._authPrompt.addCharacter(unichar);
|
||||
},
|
||||
|
||||
finish: function(onComplete) {
|
||||
this._authPrompt.finish(onComplete);
|
||||
},
|
||||
|
||||
open: function(timestamp) {
|
||||
this.actor.show();
|
||||
|
||||
if (this._isModal)
|
||||
return true;
|
||||
|
||||
if (!Main.pushModal(this.actor, { timestamp: timestamp,
|
||||
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN }))
|
||||
return false;
|
||||
|
||||
this._isModal = true;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
popModal: function(timestamp) {
|
||||
if (this._isModal) {
|
||||
Main.popModal(this.actor, timestamp);
|
||||
this._isModal = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(UnlockDialog.prototype);
|
||||
|
@ -1,944 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
const Atk = imports.gi.Atk;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
|
||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
|
||||
const IMStatus = {
|
||||
AVAILABLE: 0,
|
||||
BUSY: 1,
|
||||
HIDDEN: 2,
|
||||
AWAY: 3,
|
||||
IDLE: 4,
|
||||
OFFLINE: 5,
|
||||
LAST: 6
|
||||
};
|
||||
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<property name="Remote" type="b" access="read"/>
|
||||
<property name="Class" type="s" access="read"/>
|
||||
<property name="Type" type="s" access="read"/>
|
||||
<property name="State" type="s" access="read"/>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||
|
||||
const IMStatusItem = new Lang.Class({
|
||||
Name: 'IMStatusItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function(label, iconName) {
|
||||
this.parent();
|
||||
|
||||
this.actor.add_style_class_name('status-chooser-status-item');
|
||||
|
||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
this.addActor(this._icon);
|
||||
|
||||
if (iconName)
|
||||
this._icon.icon_name = iconName;
|
||||
|
||||
this.label = new St.Label({ text: label });
|
||||
this.actor.label_actor = this.label;
|
||||
this.addActor(this.label);
|
||||
}
|
||||
});
|
||||
|
||||
const IMUserNameItem = new Lang.Class({
|
||||
Name: 'IMUserNameItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ reactive: false,
|
||||
can_focus: false,
|
||||
style_class: 'status-chooser-user-name' });
|
||||
|
||||
this._wrapper = new Shell.GenericContainer();
|
||||
this._wrapper.connect('get-preferred-width',
|
||||
Lang.bind(this, this._wrapperGetPreferredWidth));
|
||||
this._wrapper.connect('get-preferred-height',
|
||||
Lang.bind(this, this._wrapperGetPreferredHeight));
|
||||
this._wrapper.connect('allocate',
|
||||
Lang.bind(this, this._wrapperAllocate));
|
||||
this.addActor(this._wrapper, { expand: true, span: -1 });
|
||||
|
||||
this.label = new St.Label();
|
||||
this.label.clutter_text.set_line_wrap(true);
|
||||
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
||||
this._wrapper.add_actor(this.label);
|
||||
},
|
||||
|
||||
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
|
||||
alloc.min_size = 1;
|
||||
alloc.natural_size = 1;
|
||||
},
|
||||
|
||||
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
|
||||
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
|
||||
},
|
||||
|
||||
_wrapperAllocate: function(actor, box, flags) {
|
||||
this.label.allocate(box, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const IMStatusChooserItem = new Lang.Class({
|
||||
Name: 'IMStatusChooserItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ reactive: false,
|
||||
can_focus: false,
|
||||
style_class: 'status-chooser' });
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
|
||||
this._avatar = new UserWidget.Avatar(this._user, { reactive: true });
|
||||
this._iconBin = new St.Button({ child: this._avatar.actor });
|
||||
this.addActor(this._iconBin);
|
||||
|
||||
this._iconBin.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this.activate();
|
||||
}));
|
||||
|
||||
this._section = new PopupMenu.PopupMenuSection();
|
||||
this.addActor(this._section.actor);
|
||||
|
||||
this._name = new IMUserNameItem();
|
||||
this._section.addMenuItem(this._name);
|
||||
|
||||
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
|
||||
this._section.addMenuItem(this._combo);
|
||||
|
||||
let item;
|
||||
|
||||
item = new IMStatusItem(_("Available"), 'user-available-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
|
||||
|
||||
item = new IMStatusItem(_("Busy"), 'user-busy-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.BUSY);
|
||||
|
||||
item = new IMStatusItem(_("Invisible"), 'user-invisible-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.HIDDEN);
|
||||
|
||||
item = new IMStatusItem(_("Away"), 'user-away-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.AWAY);
|
||||
|
||||
item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.IDLE);
|
||||
|
||||
item = new IMStatusItem(_("Offline"), 'user-offline-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.OFFLINE);
|
||||
|
||||
this._combo.connect('active-item-changed',
|
||||
Lang.bind(this, this._changeIMStatus));
|
||||
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
|
||||
this._sessionStatusChanged(status);
|
||||
}));
|
||||
|
||||
this._sessionPresenceRestored = false;
|
||||
this._imPresenceRestored = false;
|
||||
this._currentPresence = undefined;
|
||||
|
||||
this._accountMgr = Tp.AccountManager.dup();
|
||||
this._accountMgr.connect('most-available-presence-changed',
|
||||
Lang.bind(this, this._IMStatusChanged));
|
||||
this._accountMgr.connect('account-enabled',
|
||||
Lang.bind(this, this._IMAccountsChanged));
|
||||
this._accountMgr.connect('account-disabled',
|
||||
Lang.bind(this, this._IMAccountsChanged));
|
||||
this._accountMgr.connect('account-removed',
|
||||
Lang.bind(this, this._IMAccountsChanged));
|
||||
this._accountMgr.connect('account-validity-changed',
|
||||
Lang.bind(this, this._IMAccountsChanged));
|
||||
this._accountMgr.prepare_async(null, Lang.bind(this,
|
||||
function(mgr) {
|
||||
this._IMAccountsChanged(mgr);
|
||||
|
||||
if (this._networkMonitor.network_available)
|
||||
this._restorePresence();
|
||||
else
|
||||
this._setComboboxPresence(Tp.ConnectionPresenceType.OFFLINE);
|
||||
}));
|
||||
|
||||
this._networkMonitor = Gio.NetworkMonitor.get_default();
|
||||
this._networkMonitor.connect('network-changed',
|
||||
Lang.bind(this, function(monitor, available) {
|
||||
this._IMAccountsChanged(this._accountMgr);
|
||||
|
||||
if (available && !this._imPresenceRestored)
|
||||
this._restorePresence();
|
||||
}));
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||
Lang.bind(this,
|
||||
this._updateUser));
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this,
|
||||
this._updateUser));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, function() {
|
||||
if (this.actor.mapped)
|
||||
this._updateUser();
|
||||
}));
|
||||
|
||||
this.connect('sensitive-changed', function(sensitive) {
|
||||
this._avatar.setSensitive(sensitive);
|
||||
});
|
||||
},
|
||||
|
||||
_restorePresence: function() {
|
||||
let [presence, status, msg] = this._accountMgr.get_most_available_presence();
|
||||
|
||||
let savedPresence = global.settings.get_int('saved-im-presence');
|
||||
|
||||
if (savedPresence == presence) {
|
||||
this._IMStatusChanged(this._accountMgr, presence, status, msg);
|
||||
} else {
|
||||
this._setComboboxPresence(savedPresence);
|
||||
status = this._statusForPresence(savedPresence);
|
||||
msg = msg ? msg : '';
|
||||
this._accountMgr.set_all_requested_presences(savedPresence, status, msg);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
// clean up signal handlers
|
||||
if (this._userLoadedId != 0) {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._userLoadedId = 0;
|
||||
}
|
||||
|
||||
if (this._userChangedId != 0) {
|
||||
this._user.disconnect(this._userChangedId);
|
||||
this._userChangedId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
// Override getColumnWidths()/setColumnWidths() to make the item
|
||||
// independent from the overall column layout of the menu
|
||||
getColumnWidths: function() {
|
||||
return [];
|
||||
},
|
||||
|
||||
setColumnWidths: function(widths) {
|
||||
},
|
||||
|
||||
_updateUser: function() {
|
||||
if (this._user.is_loaded)
|
||||
this._name.label.set_text(this._user.get_real_name());
|
||||
else
|
||||
this._name.label.set_text("");
|
||||
|
||||
this._avatar.update();
|
||||
},
|
||||
|
||||
_statusForPresence: function(presence) {
|
||||
switch(presence) {
|
||||
case Tp.ConnectionPresenceType.AVAILABLE:
|
||||
return 'available';
|
||||
case Tp.ConnectionPresenceType.BUSY:
|
||||
return 'busy';
|
||||
case Tp.ConnectionPresenceType.OFFLINE:
|
||||
return 'offline';
|
||||
case Tp.ConnectionPresenceType.HIDDEN:
|
||||
return 'hidden';
|
||||
case Tp.ConnectionPresenceType.AWAY:
|
||||
return 'away';
|
||||
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
||||
return 'xa';
|
||||
default:
|
||||
return 'unknown';
|
||||
}
|
||||
},
|
||||
|
||||
_IMAccountsChanged: function(mgr) {
|
||||
let accounts = mgr.get_valid_accounts().filter(function(account) {
|
||||
return account.enabled;
|
||||
});
|
||||
let sensitive = accounts.length > 0 && this._networkMonitor.network_available;
|
||||
this._combo.setSensitive(sensitive);
|
||||
},
|
||||
|
||||
_IMStatusChanged: function(accountMgr, presence, status, message) {
|
||||
if (!this._imPresenceRestored)
|
||||
this._imPresenceRestored = true;
|
||||
|
||||
if (presence == this._currentPresence)
|
||||
return;
|
||||
|
||||
this._currentPresence = presence;
|
||||
this._setComboboxPresence(presence);
|
||||
|
||||
if (!this._sessionPresenceRestored) {
|
||||
this._sessionStatusChanged(this._presence.status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
this._presence.status = GnomeSession.PresenceStatus.AVAILABLE;
|
||||
|
||||
// We ignore the actual value of _expectedPresence and never safe
|
||||
// the first presence change after an "automatic" change, assuming
|
||||
// that it is the response to our request; this is to account for
|
||||
// mission control falling back to "similar" presences if an account
|
||||
// type does not implement the requested presence.
|
||||
if (!this._expectedPresence)
|
||||
global.settings.set_int('saved-im-presence', presence);
|
||||
else
|
||||
this._expectedPresence = undefined;
|
||||
},
|
||||
|
||||
_setComboboxPresence: function(presence) {
|
||||
let activatedItem;
|
||||
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
activatedItem = IMStatus.AVAILABLE;
|
||||
else if (presence == Tp.ConnectionPresenceType.BUSY)
|
||||
activatedItem = IMStatus.BUSY;
|
||||
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
|
||||
activatedItem = IMStatus.HIDDEN;
|
||||
else if (presence == Tp.ConnectionPresenceType.AWAY)
|
||||
activatedItem = IMStatus.AWAY;
|
||||
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
||||
activatedItem = IMStatus.IDLE;
|
||||
else
|
||||
activatedItem = IMStatus.OFFLINE;
|
||||
|
||||
this._combo.setActiveItem(activatedItem);
|
||||
for (let i = 0; i < IMStatus.LAST; i++) {
|
||||
if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
|
||||
continue; // always visible
|
||||
|
||||
this._combo.setItemVisible(i, i == activatedItem);
|
||||
}
|
||||
},
|
||||
|
||||
_changeIMStatus: function(menuItem, id) {
|
||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||
let newPresence, status;
|
||||
|
||||
if (id == IMStatus.AVAILABLE) {
|
||||
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
|
||||
} else if (id == IMStatus.OFFLINE) {
|
||||
newPresence = Tp.ConnectionPresenceType.OFFLINE;
|
||||
} else
|
||||
return;
|
||||
|
||||
status = this._statusForPresence(newPresence);
|
||||
msg = msg ? msg : '';
|
||||
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
|
||||
},
|
||||
|
||||
getIMPresenceForSessionStatus: function(sessionStatus) {
|
||||
// Restore the last user-set presence when coming back from
|
||||
// BUSY/IDLE (otherwise the last user-set presence matches
|
||||
// the current one)
|
||||
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
|
||||
return global.settings.get_int('saved-im-presence');
|
||||
|
||||
if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
|
||||
// Only change presence if the current one is "more present" than
|
||||
// busy, or if coming back from idle
|
||||
if (this._currentPresence == Tp.ConnectionPresenceType.AVAILABLE ||
|
||||
this._currentPresence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
||||
return Tp.ConnectionPresenceType.BUSY;
|
||||
}
|
||||
|
||||
if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
|
||||
// Only change presence if the current one is "more present" than
|
||||
// idle
|
||||
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
|
||||
this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
|
||||
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
|
||||
}
|
||||
|
||||
return this._currentPresence;
|
||||
},
|
||||
|
||||
_sessionStatusChanged: function(sessionStatus) {
|
||||
if (!this._imPresenceRestored)
|
||||
return;
|
||||
|
||||
let savedStatus = global.settings.get_int('saved-session-presence');
|
||||
if (!this._sessionPresenceRestored) {
|
||||
|
||||
// We should never save/restore a status other than AVAILABLE
|
||||
// or BUSY
|
||||
if (savedStatus != GnomeSession.PresenceStatus.AVAILABLE &&
|
||||
savedStatus != GnomeSession.PresenceStatus.BUSY)
|
||||
savedStatus = GnomeSession.PresenceStatus.AVAILABLE;
|
||||
|
||||
if (sessionStatus != savedStatus) {
|
||||
this._presence.status = savedStatus;
|
||||
return;
|
||||
}
|
||||
this._sessionPresenceRestored = true;
|
||||
}
|
||||
|
||||
if ((sessionStatus == GnomeSession.PresenceStatus.AVAILABLE ||
|
||||
sessionStatus == GnomeSession.PresenceStatus.BUSY) &&
|
||||
savedStatus != sessionStatus)
|
||||
global.settings.set_int('saved-session-presence', sessionStatus);
|
||||
|
||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||
let newPresence, status;
|
||||
|
||||
let newPresence = this.getIMPresenceForSessionStatus(sessionStatus);
|
||||
|
||||
if (!newPresence || newPresence == presence)
|
||||
return;
|
||||
|
||||
status = this._statusForPresence(newPresence);
|
||||
msg = msg ? msg : '';
|
||||
|
||||
this._expectedPresence = newPresence;
|
||||
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const UserMenuButton = new Lang.Class({
|
||||
Name: 'UserMenuButton',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0);
|
||||
|
||||
this.actor.accessible_role = Atk.Role.MENU;
|
||||
|
||||
let box = new St.BoxLayout({ name: 'panelUserMenu' });
|
||||
this.actor.add_actor(box);
|
||||
|
||||
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._haveShutdown = true;
|
||||
this._haveSuspend = true;
|
||||
|
||||
this._accountMgr = Tp.AccountManager.dup();
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
this._offlineIcon = new St.Icon({ icon_name: 'user-offline-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._availableIcon = new St.Icon({ icon_name: 'user-available-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._busyIcon = new St.Icon({ icon_name: 'user-busy-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._awayIcon = new St.Icon({ icon_name: 'user-away-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._idleIcon = new St.Icon({ icon_name: 'user-idle-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
|
||||
this._accountMgr.connect('most-available-presence-changed',
|
||||
Lang.bind(this, this._updatePresenceIcon));
|
||||
this._accountMgr.connect('account-enabled',
|
||||
Lang.bind(this, this._onAccountEnabled));
|
||||
this._accountMgr.connect('account-removed',
|
||||
Lang.bind(this, this._onAccountRemoved));
|
||||
this._accountMgr.prepare_async(null, Lang.bind(this,
|
||||
function(mgr) {
|
||||
let [presence, s, msg] = mgr.get_most_available_presence();
|
||||
this._updatePresenceIcon(mgr, presence, s, msg);
|
||||
this._setupAccounts();
|
||||
}));
|
||||
|
||||
this._name = new St.Label();
|
||||
this.actor.label_actor = this._name;
|
||||
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
|
||||
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
|
||||
this._updateUserName();
|
||||
|
||||
this._createSubMenu();
|
||||
|
||||
this._updateSwitch(this._presence.status);
|
||||
this._presence.connectSignal('StatusChanged', Lang.bind(this, function (proxy, senderName, [status]) {
|
||||
this._updateSwitch(status);
|
||||
}));
|
||||
|
||||
this._userManager.connect('notify::is-loaded',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('notify::has-multiple-users',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('user-added',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('user-removed',
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateLogout));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||
Lang.bind(this, this._updateLockScreen));
|
||||
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateLogout));
|
||||
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
||||
Lang.bind(this, this._updateUserName));
|
||||
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
||||
Lang.bind(this, this._updateUserName));
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
this._updateLockScreen();
|
||||
|
||||
this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
|
||||
this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||
this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
|
||||
|
||||
// Whether shutdown is available or not depends on both lockdown
|
||||
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
||||
// notify, so we update the menu item each time the menu opens or
|
||||
// the lockdown setting changes, which should be close enough.
|
||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||
function(menu, open) {
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveSuspend();
|
||||
}));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this.actor.visible = !Main.sessionMode.isGreeter;
|
||||
|
||||
let allowSettings = Main.sessionMode.allowSettings;
|
||||
this._statusChooser.setSensitive(allowSettings);
|
||||
|
||||
this.setSensitive(!Main.sessionMode.isLocked);
|
||||
this._updatePresenceIcon();
|
||||
this._updateUserName();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_updateUserName: function() {
|
||||
let settings = this._privacySettings;
|
||||
if (Main.sessionMode.isLocked)
|
||||
settings = this._screenSaverSettings;
|
||||
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
|
||||
this._name.set_text(this._user.get_real_name());
|
||||
else
|
||||
this._name.set_text("");
|
||||
},
|
||||
|
||||
_updateMultiUser: function() {
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
},
|
||||
|
||||
_updateSwitchUser: function() {
|
||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
|
||||
|
||||
this._loginScreenItem.actor.visible = allowSwitch && multiUser;
|
||||
},
|
||||
|
||||
_updateLogout: function() {
|
||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
|
||||
let systemAccount = this._user.system_account;
|
||||
let localAccount = this._user.local_account;
|
||||
let multiUser = this._userManager.has_multiple_users;
|
||||
let multiSession = Gdm.get_session_ids().length > 1;
|
||||
|
||||
this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock();
|
||||
},
|
||||
|
||||
_updateInstallUpdates: function() {
|
||||
let haveUpdates = this._updatesFile.query_exists(null);
|
||||
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._session.CanShutdownRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._haveShutdown = result[0];
|
||||
this._updateInstallUpdates();
|
||||
this._updateSuspendOrPowerOff();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._loginManager.canSuspend(Lang.bind(this,
|
||||
function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._updateSuspendOrPowerOff();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspendOrPowerOff: function() {
|
||||
if (!this._suspendOrPowerOffItem)
|
||||
return;
|
||||
|
||||
this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend;
|
||||
|
||||
// If we can't power off show Suspend instead
|
||||
// and disable the alt key
|
||||
if (!this._haveShutdown) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
|
||||
} else if (!this._haveSuspend) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off"), null);
|
||||
} else {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend"));
|
||||
}
|
||||
},
|
||||
|
||||
_updateSwitch: function(status) {
|
||||
let active = status == GnomeSession.PresenceStatus.AVAILABLE;
|
||||
this._notificationsSwitch.setToggleState(active);
|
||||
},
|
||||
|
||||
_updatePresenceIcon: function(accountMgr, presence, status, message) {
|
||||
if (Main.sessionMode.isLocked)
|
||||
this._iconBox.child = this._lockedIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
this._iconBox.child = this._availableIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.BUSY)
|
||||
this._iconBox.child = this._busyIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
|
||||
this._iconBox.child = this._invisibleIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.AWAY)
|
||||
this._iconBox.child = this._awayIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
||||
this._iconBox.child = this._idleIcon;
|
||||
else
|
||||
this._iconBox.child = this._offlineIcon;
|
||||
|
||||
if (Main.sessionMode.isLocked)
|
||||
this._iconBox.visible = Main.screenShield.locked;
|
||||
else
|
||||
this._iconBox.visible = true;
|
||||
},
|
||||
|
||||
_setupAccounts: function() {
|
||||
let accounts = this._accountMgr.get_valid_accounts();
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
accounts[i]._changingId = accounts[i].connect('notify::connection-status',
|
||||
Lang.bind(this, this._updateChangingPresence));
|
||||
}
|
||||
this._updateChangingPresence();
|
||||
},
|
||||
|
||||
_onAccountEnabled: function(accountMgr, account) {
|
||||
if (!account._changingId)
|
||||
account._changingId = account.connect('notify::connection-status',
|
||||
Lang.bind(this, this._updateChangingPresence));
|
||||
this._updateChangingPresence();
|
||||
},
|
||||
|
||||
_onAccountRemoved: function(accountMgr, account) {
|
||||
if (account._changingId) {
|
||||
account.disconnect(account._changingId);
|
||||
account._changingId = 0;
|
||||
}
|
||||
this._updateChangingPresence();
|
||||
},
|
||||
|
||||
_updateChangingPresence: function() {
|
||||
let accounts = this._accountMgr.get_valid_accounts();
|
||||
let changing = false;
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
|
||||
changing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (changing) {
|
||||
this._iconBox.child = this._pendingIcon;
|
||||
} else {
|
||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
|
||||
}
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
item = new IMStatusChooserItem();
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._statusChooser = item;
|
||||
|
||||
item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
|
||||
item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
|
||||
this.menu.addMenuItem(item);
|
||||
this._notificationsSwitch = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
this.menu.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._loginScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Log Out"));
|
||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._logoutItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Lock"));
|
||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._lockScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
|
||||
_("Suspend"));
|
||||
this.menu.addMenuItem(item);
|
||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
||||
this._suspendOrPowerOffItem = item;
|
||||
this._updateSuspendOrPowerOff();
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
|
||||
item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._installUpdatesItem = item;
|
||||
},
|
||||
|
||||
_updatePresenceStatus: function(item, event) {
|
||||
let status;
|
||||
|
||||
if (item.state) {
|
||||
status = GnomeSession.PresenceStatus.AVAILABLE;
|
||||
} else {
|
||||
status = GnomeSession.PresenceStatus.BUSY;
|
||||
|
||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||
let newPresence = this._statusChooser.getIMPresenceForSessionStatus(status);
|
||||
if (newPresence != presence &&
|
||||
newPresence == Tp.ConnectionPresenceType.BUSY)
|
||||
Main.notify(_("Your chat status will be set to busy"),
|
||||
_("Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."));
|
||||
}
|
||||
|
||||
this._presence.status = status;
|
||||
},
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
Main.screenShield.lock(true);
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.lock(false);
|
||||
Gdm.goto_login_session_sync(null);
|
||||
},
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._session.LogoutRemote(0);
|
||||
},
|
||||
|
||||
_onInstallUpdatesActivate: function() {
|
||||
Main.overview.hide();
|
||||
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
|
||||
|
||||
this._session.RebootRemote();
|
||||
},
|
||||
|
||||
_openSessionWarnDialog: function(sessions) {
|
||||
let dialog = new ModalDialog.ModalDialog();
|
||||
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
|
||||
text: _("Other users are logged in.") });
|
||||
dialog.contentLayout.add(subjectLabel, { y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
|
||||
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
|
||||
descriptionLabel.clutter_text.line_wrap = true;
|
||||
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
|
||||
y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
|
||||
scrollView.add_style_class_name('vfade');
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
|
||||
|
||||
let userList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(userList);
|
||||
|
||||
for (let i = 0; i < sessions.length; i++) {
|
||||
let session = sessions[i];
|
||||
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
|
||||
vertical: false });
|
||||
let avatar = new UserWidget.Avatar(session.user);
|
||||
avatar.update();
|
||||
userEntry.add(avatar.actor);
|
||||
|
||||
let userLabelText = "";;
|
||||
let userName = session.user.get_real_name() ?
|
||||
session.user.get_real_name() : session.username;
|
||||
|
||||
if (session.info.remote)
|
||||
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||
userLabelText = _("%s (remote)").format(userName);
|
||||
else if (session.info.type == "tty")
|
||||
/* Translators: Console here refers to a tty like a VT console */
|
||||
userLabelText = _("%s (console)").format(userName);
|
||||
else
|
||||
userLabelText = userName;
|
||||
|
||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||
vertical: true });
|
||||
textLayout.add(new St.Label({ text: userLabelText }),
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
expand: true });
|
||||
userEntry.add(textLayout, { expand: true });
|
||||
userList.add(userEntry, { x_fill: true });
|
||||
}
|
||||
|
||||
let cancelButton = { label: _("Cancel"),
|
||||
action: function() { dialog.close(); },
|
||||
key: Clutter.Escape };
|
||||
|
||||
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
|
||||
dialog.close();
|
||||
this._session.ShutdownRemote();
|
||||
}), default: true };
|
||||
|
||||
dialog.setButtons([cancelButton, powerOffButton]);
|
||||
|
||||
dialog.open();
|
||||
},
|
||||
|
||||
_onSuspendOrPowerOffActivate: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
if (this._haveShutdown &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
this._loginManager.listSessions(Lang.bind(this,
|
||||
function(result) {
|
||||
let sessions = [];
|
||||
let n = 0;
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let[id, uid, userName, seat, sessionPath] = result[i];
|
||||
let proxy = new SystemdLoginSession(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
sessionPath);
|
||||
|
||||
if (proxy.Class != 'user')
|
||||
continue;
|
||||
|
||||
if (proxy.State == 'closing')
|
||||
continue;
|
||||
|
||||
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
||||
continue;
|
||||
|
||||
sessions.push({ user: this._userManager.get_user(userName),
|
||||
username: userName,
|
||||
info: { type: proxy.Type,
|
||||
remote: proxy.Remote }
|
||||
});
|
||||
|
||||
// limit the number of entries
|
||||
n++;
|
||||
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
||||
break;
|
||||
}
|
||||
|
||||
if (n != 0)
|
||||
this._openSessionWarnDialog(sessions);
|
||||
else
|
||||
this._session.ShutdownRemote();
|
||||
}));
|
||||
} else {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
this._loginManager.suspend();
|
||||
}
|
||||
}
|
||||
});
|
@ -24,7 +24,7 @@ const Avatar = new Lang.Class({
|
||||
this._user = user;
|
||||
params = Params.parse(params, { reactive: false,
|
||||
iconSize: AVATAR_ICON_SIZE,
|
||||
styleClass: 'status-chooser-user-icon' });
|
||||
styleClass: 'framed-user-icon' });
|
||||
this._iconSize = params.iconSize;
|
||||
|
||||
this.actor = new St.Bin({ style_class: params.styleClass,
|
||||
|
@ -473,12 +473,20 @@ const WindowManager = new Lang.Class({
|
||||
this._dimWindow(this._dimmedWindows[i]);
|
||||
}));
|
||||
|
||||
this._workspaceTracker = new WorkspaceTracker(this);
|
||||
if (Main.sessionMode.hasWorkspaces)
|
||||
this._workspaceTracker = new WorkspaceTracker(this);
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
},
|
||||
|
||||
keepWorkspaceAlive: function(workspace, duration) {
|
||||
if (!this._workspaceTracker)
|
||||
return;
|
||||
|
||||
this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
|
||||
},
|
||||
|
||||
setCustomKeybindingHandler: function(name, modes, handler) {
|
||||
if (Meta.keybindings_set_custom_handler(name, handler))
|
||||
this.allowKeybinding(name, modes);
|
||||
@ -825,7 +833,7 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_switchWorkspace : function(shellwm, from, to, direction) {
|
||||
if (!this._shouldAnimate()) {
|
||||
if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) {
|
||||
shellwm.completed_switch_workspace();
|
||||
return;
|
||||
}
|
||||
@ -978,6 +986,9 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_showWorkspaceSwitcher : function(display, screen, window, binding) {
|
||||
if (!Main.sessionMode.hasWorkspaces)
|
||||
return;
|
||||
|
||||
if (screen.n_workspaces == 1)
|
||||
return;
|
||||
|
||||
@ -1019,14 +1030,19 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
actionMoveWorkspace: function(workspace) {
|
||||
if (!Main.sessionMode.hasWorkspaces)
|
||||
return;
|
||||
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
|
||||
if (activeWorkspace != workspace)
|
||||
workspace.activate(global.get_current_time());
|
||||
|
||||
},
|
||||
|
||||
actionMoveWindow: function(window, workspace) {
|
||||
if (!Main.sessionMode.hasWorkspaces)
|
||||
return;
|
||||
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
|
||||
if (activeWorkspace != workspace) {
|
||||
@ -1039,6 +1055,5 @@ const WindowManager = new Lang.Class({
|
||||
global.display.clear_mouse_mode();
|
||||
workspace.activate_with_focus (window, global.get_current_time());
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
@ -127,7 +127,7 @@ const WindowClone = new Lang.Class({
|
||||
if (this._stackAbove == null)
|
||||
return null;
|
||||
|
||||
if (this.inDrag || this._zooming) {
|
||||
if (this.inDrag) {
|
||||
if (this._stackAbove._delegate)
|
||||
return this._stackAbove._delegate.getActualStackAbove();
|
||||
else
|
||||
@ -997,7 +997,7 @@ const Workspace = new Lang.Class({
|
||||
|
||||
this._dropRect.set_position(geom.x, geom.y);
|
||||
this._dropRect.set_size(geom.width, geom.height);
|
||||
this._updateWindowPositions(WindowPositionFlags.NONE);
|
||||
this._updateWindowPositions(Main.overview.animationInProgress ? WindowPositionFlags.ANIMATE : WindowPositionFlags.NONE);
|
||||
|
||||
this._actualGeometryLater = 0;
|
||||
return false;
|
||||
|
@ -764,8 +764,8 @@ const ThumbnailsBox = new Lang.Class({
|
||||
// to open its first window within some time, as tracked by Shell.WindowTracker.
|
||||
// Here, we only add a very brief timeout to avoid the _immediate_ removal of the
|
||||
// workspace while we wait for the startup sequence to load.
|
||||
Main.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
|
||||
WORKSPACE_KEEP_ALIVE_TIME);
|
||||
Main.wm.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
|
||||
WORKSPACE_KEEP_ALIVE_TIME);
|
||||
}
|
||||
|
||||
// Start the animation on the workspace (which is actually
|
||||
|
@ -7,6 +7,7 @@ data/gnome-shell.desktop.in.in
|
||||
data/gnome-shell-extension-prefs.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in.in
|
||||
js/extensionPrefs/main.js
|
||||
js/gdm/authPrompt.js
|
||||
js/gdm/loginDialog.js
|
||||
js/gdm/powerMenu.js
|
||||
js/gdm/util.js
|
||||
@ -48,9 +49,9 @@ js/ui/status/keyboard.js
|
||||
js/ui/status/lockScreenMenu.js
|
||||
js/ui/status/network.js
|
||||
js/ui/status/power.js
|
||||
js/ui/status/system.js
|
||||
js/ui/status/volume.js
|
||||
js/ui/unlockDialog.js
|
||||
js/ui/userMenu.js
|
||||
js/ui/viewSelector.js
|
||||
js/ui/wanda.js
|
||||
js/ui/windowAttentionHandler.js
|
||||
|
369
po/gl.po
369
po/gl.po
@ -11,8 +11,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-05-31 01:03+0200\n"
|
||||
"PO-Revision-Date: 2013-05-31 01:04+0200\n"
|
||||
"POT-Creation-Date: 2013-06-28 01:33+0200\n"
|
||||
"PO-Revision-Date: 2013-06-28 01:35+0200\n"
|
||||
"Last-Translator: Fran Dieguez <frandieguez@gnome.org>\n"
|
||||
"Language-Team: gnome-l10n-gl@gnome.org\n"
|
||||
"Language: gl\n"
|
||||
@ -369,37 +369,41 @@ msgid "Select an extension to configure using the combobox above."
|
||||
msgstr ""
|
||||
"Seleccione unha extensión que configurar usando a caixa combinada de arriba."
|
||||
|
||||
#: ../js/gdm/loginDialog.js:371
|
||||
msgid "Session…"
|
||||
msgstr "Sesión…"
|
||||
#: ../js/gdm/loginDialog.js:308
|
||||
msgid "Choose Session"
|
||||
msgstr "Escolla unha sesión"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:326
|
||||
msgid "Session"
|
||||
msgstr "Sesión"
|
||||
|
||||
#. translators: this message is shown below the user list on the
|
||||
#. login screen. It can be activated to reveal an entry for
|
||||
#. manually entering the username.
|
||||
#: ../js/gdm/loginDialog.js:601
|
||||
#: ../js/gdm/loginDialog.js:528
|
||||
msgid "Not listed?"
|
||||
msgstr "Non está na lista?"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:776 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
|
||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:96
|
||||
#: ../js/ui/userMenu.js:938
|
||||
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
|
||||
#: ../js/ui/userMenu.js:884
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:791
|
||||
#: ../js/gdm/loginDialog.js:833
|
||||
msgctxt "button"
|
||||
msgid "Sign In"
|
||||
msgstr "Iniciar sesión"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:791
|
||||
#: ../js/gdm/loginDialog.js:833
|
||||
msgid "Next"
|
||||
msgstr "Seguinte"
|
||||
|
||||
#. Translators: this message is shown below the username entry field
|
||||
#. to clue the user in on how to login to the local network realm
|
||||
#: ../js/gdm/loginDialog.js:888
|
||||
#: ../js/gdm/loginDialog.js:934
|
||||
#, c-format
|
||||
msgid "(e.g., user or %s)"
|
||||
msgstr "(p.ex., usuario ou %s)"
|
||||
@ -407,12 +411,12 @@ msgstr "(p.ex., usuario ou %s)"
|
||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||
#. is not visible here since we only care about phase2 authentication
|
||||
#. (and don't even care of which one)
|
||||
#: ../js/gdm/loginDialog.js:892 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/ui/components/networkAgent.js:278
|
||||
msgid "Username: "
|
||||
msgstr "Nome de usuario: "
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1158
|
||||
#: ../js/gdm/loginDialog.js:1205
|
||||
msgid "Login Window"
|
||||
msgstr "Xanela de inicio de sesión"
|
||||
|
||||
@ -421,8 +425,8 @@ msgstr "Xanela de inicio de sesión"
|
||||
msgid "Power"
|
||||
msgstr "Apagar"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
|
||||
#: ../js/ui/userMenu.js:816
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655
|
||||
#: ../js/ui/userMenu.js:768
|
||||
msgid "Suspend"
|
||||
msgstr "Suspender"
|
||||
|
||||
@ -430,18 +434,18 @@ msgstr "Suspender"
|
||||
msgid "Restart"
|
||||
msgstr "Reiniciar"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
|
||||
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653
|
||||
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888
|
||||
msgid "Power Off"
|
||||
msgstr "Apagar"
|
||||
|
||||
#: ../js/gdm/util.js:247
|
||||
#: ../js/gdm/util.js:248
|
||||
msgid "Authentication error"
|
||||
msgstr "Erro de autenticación"
|
||||
|
||||
#. Translators: this message is shown below the password entry field
|
||||
#. to indicate the user can swipe their finger instead
|
||||
#: ../js/gdm/util.js:364
|
||||
#: ../js/gdm/util.js:365
|
||||
msgid "(or swipe finger)"
|
||||
msgstr "(ou pase o dedo)"
|
||||
|
||||
@ -460,23 +464,23 @@ msgstr "Non foi posíbel analizar a orde:"
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Produciuse un fallo na execución de «%s»:"
|
||||
|
||||
#: ../js/ui/appDisplay.js:361
|
||||
#: ../js/ui/appDisplay.js:397
|
||||
msgid "Frequent"
|
||||
msgstr "Frecuentes"
|
||||
|
||||
#: ../js/ui/appDisplay.js:368
|
||||
#: ../js/ui/appDisplay.js:404
|
||||
msgid "All"
|
||||
msgstr "Todos"
|
||||
|
||||
#: ../js/ui/appDisplay.js:960
|
||||
#: ../js/ui/appDisplay.js:996
|
||||
msgid "New Window"
|
||||
msgstr "Xanela nova"
|
||||
|
||||
#: ../js/ui/appDisplay.js:963 ../js/ui/dash.js:284
|
||||
#: ../js/ui/appDisplay.js:999 ../js/ui/dash.js:284
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Retirar dos marcadores"
|
||||
|
||||
#: ../js/ui/appDisplay.js:964
|
||||
#: ../js/ui/appDisplay.js:1000
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Engadir aos favoritos"
|
||||
|
||||
@ -490,7 +494,7 @@ msgstr "%s foi engadido aos seus favoritos."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s retirouse dos seus marcadores."
|
||||
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744
|
||||
msgid "Settings"
|
||||
msgstr "Preferencias"
|
||||
|
||||
@ -615,35 +619,35 @@ msgid "S"
|
||||
msgstr "S"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:735
|
||||
#: ../js/ui/calendar.js:750
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Nada programado"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:768
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %d de %B"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:754
|
||||
#: ../js/ui/calendar.js:771
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, %d de %B de %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:764
|
||||
#: ../js/ui/calendar.js:782
|
||||
msgid "Today"
|
||||
msgstr "Hoxe"
|
||||
|
||||
#: ../js/ui/calendar.js:768
|
||||
#: ../js/ui/calendar.js:786
|
||||
msgid "Tomorrow"
|
||||
msgstr "Mañá"
|
||||
|
||||
#: ../js/ui/calendar.js:779
|
||||
#: ../js/ui/calendar.js:797
|
||||
msgid "This week"
|
||||
msgstr "Esta semana"
|
||||
|
||||
#: ../js/ui/calendar.js:787
|
||||
#: ../js/ui/calendar.js:805
|
||||
msgid "Next week"
|
||||
msgstr "A vindeira semana"
|
||||
|
||||
@ -832,14 +836,14 @@ msgstr "<b>%d</b> de <b>%B</b> de <b>%Y</b>, <b>%H:%M</b> "
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/components/telepathyClient.js:985
|
||||
#: ../js/ui/components/telepathyClient.js:986
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "Agora %s chámase %s"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1088
|
||||
#: ../js/ui/components/telepathyClient.js:1089
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "Convite a %s"
|
||||
@ -847,38 +851,38 @@ msgstr "Convite a %s"
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1096
|
||||
#: ../js/ui/components/telepathyClient.js:1097
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "%s estalle convidando a unirse a %s"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1098
|
||||
#: ../js/ui/components/telepathyClient.js:1137
|
||||
#: ../js/ui/components/telepathyClient.js:1177
|
||||
#: ../js/ui/components/telepathyClient.js:1240
|
||||
#: ../js/ui/components/telepathyClient.js:1099
|
||||
#: ../js/ui/components/telepathyClient.js:1138
|
||||
#: ../js/ui/components/telepathyClient.js:1178
|
||||
#: ../js/ui/components/telepathyClient.js:1241
|
||||
msgid "Decline"
|
||||
msgstr "Rexeitar"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1099
|
||||
#: ../js/ui/components/telepathyClient.js:1178
|
||||
#: ../js/ui/components/telepathyClient.js:1241
|
||||
#: ../js/ui/components/telepathyClient.js:1100
|
||||
#: ../js/ui/components/telepathyClient.js:1179
|
||||
#: ../js/ui/components/telepathyClient.js:1242
|
||||
msgid "Accept"
|
||||
msgstr "Aceptar"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1129
|
||||
#: ../js/ui/components/telepathyClient.js:1130
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "Videochamada de %s"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1132
|
||||
#: ../js/ui/components/telepathyClient.js:1133
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "Chamada de %s"
|
||||
|
||||
#. translators: this is a button label (verb), not a noun
|
||||
#: ../js/ui/components/telepathyClient.js:1139
|
||||
#: ../js/ui/components/telepathyClient.js:1140
|
||||
msgid "Answer"
|
||||
msgstr "Responder"
|
||||
|
||||
@ -887,112 +891,112 @@ msgstr "Responder"
|
||||
#. * file name. The string will be something
|
||||
#. * like: "Alice is sending you test.ogg"
|
||||
#.
|
||||
#: ../js/ui/components/telepathyClient.js:1171
|
||||
#: ../js/ui/components/telepathyClient.js:1172
|
||||
#, c-format
|
||||
msgid "%s is sending you %s"
|
||||
msgstr "%s esta enviándolle %s"
|
||||
|
||||
#. To translators: The parameter is the contact's alias
|
||||
#: ../js/ui/components/telepathyClient.js:1206
|
||||
#: ../js/ui/components/telepathyClient.js:1207
|
||||
#, c-format
|
||||
msgid "%s would like permission to see when you are online"
|
||||
msgstr "%s solicítalle permiso para ver cando está en liña"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1298
|
||||
#: ../js/ui/components/telepathyClient.js:1299
|
||||
msgid "Network error"
|
||||
msgstr "Erro da rede"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1300
|
||||
#: ../js/ui/components/telepathyClient.js:1301
|
||||
msgid "Authentication failed"
|
||||
msgstr "Fallou a autenticación"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1302
|
||||
#: ../js/ui/components/telepathyClient.js:1303
|
||||
msgid "Encryption error"
|
||||
msgstr "Erro de cifrado"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1304
|
||||
#: ../js/ui/components/telepathyClient.js:1305
|
||||
msgid "Certificate not provided"
|
||||
msgstr "Certificado non fornecido"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1306
|
||||
#: ../js/ui/components/telepathyClient.js:1307
|
||||
msgid "Certificate untrusted"
|
||||
msgstr "Non se confía no certificado"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1308
|
||||
#: ../js/ui/components/telepathyClient.js:1309
|
||||
msgid "Certificate expired"
|
||||
msgstr "Certificado caducado"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1310
|
||||
#: ../js/ui/components/telepathyClient.js:1311
|
||||
msgid "Certificate not activated"
|
||||
msgstr "Certificado non activado"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1312
|
||||
#: ../js/ui/components/telepathyClient.js:1313
|
||||
msgid "Certificate hostname mismatch"
|
||||
msgstr "O nome do servidor do certificado non coincide"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1314
|
||||
#: ../js/ui/components/telepathyClient.js:1315
|
||||
msgid "Certificate fingerprint mismatch"
|
||||
msgstr "A pegada do certificado non coincide"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1316
|
||||
#: ../js/ui/components/telepathyClient.js:1317
|
||||
msgid "Certificate self-signed"
|
||||
msgstr "Certificado autoasinado"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1318
|
||||
#: ../js/ui/components/telepathyClient.js:1319
|
||||
msgid "Status is set to offline"
|
||||
msgstr "O estado está definido a «desconectado»"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1320
|
||||
#: ../js/ui/components/telepathyClient.js:1321
|
||||
msgid "Encryption is not available"
|
||||
msgstr "O cifrado non está dispoñíbel"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1322
|
||||
#: ../js/ui/components/telepathyClient.js:1323
|
||||
msgid "Certificate is invalid"
|
||||
msgstr "O certificado non é válido"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1324
|
||||
#: ../js/ui/components/telepathyClient.js:1325
|
||||
msgid "Connection has been refused"
|
||||
msgstr "Rexeitouse a conexión"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1326
|
||||
#: ../js/ui/components/telepathyClient.js:1327
|
||||
msgid "Connection can't be established"
|
||||
msgstr "Non é posíbel estabelecer a conexión"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1328
|
||||
#: ../js/ui/components/telepathyClient.js:1329
|
||||
msgid "Connection has been lost"
|
||||
msgstr "Perdeuse a conexión"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1330
|
||||
#: ../js/ui/components/telepathyClient.js:1331
|
||||
msgid "This account is already connected to the server"
|
||||
msgstr "Esta cuenta xa está conectada ao servidor"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1332
|
||||
#: ../js/ui/components/telepathyClient.js:1333
|
||||
msgid ""
|
||||
"Connection has been replaced by a new connection using the same resource"
|
||||
msgstr ""
|
||||
"Substituíuse a conexión por unha nova conexión empregando o mesmo recurso"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1334
|
||||
#: ../js/ui/components/telepathyClient.js:1335
|
||||
msgid "The account already exists on the server"
|
||||
msgstr "Esta conta xa existe no servidor"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1336
|
||||
#: ../js/ui/components/telepathyClient.js:1337
|
||||
msgid "Server is currently too busy to handle the connection"
|
||||
msgstr ""
|
||||
"Nestes intres o servidor está moi ocupado tentando xestionar a conexión"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1338
|
||||
#: ../js/ui/components/telepathyClient.js:1339
|
||||
msgid "Certificate has been revoked"
|
||||
msgstr "Revogouse o certificado"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1340
|
||||
#: ../js/ui/components/telepathyClient.js:1341
|
||||
msgid ""
|
||||
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
|
||||
msgstr ""
|
||||
"O certificado usa un algoritmo de cifrado inseguro ou é criptográficamente "
|
||||
"débil"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1342
|
||||
#: ../js/ui/components/telepathyClient.js:1343
|
||||
msgid ""
|
||||
"The length of the server certificate, or the depth of the server certificate "
|
||||
"chain, exceed the limits imposed by the cryptography library"
|
||||
@ -1001,22 +1005,22 @@ msgstr ""
|
||||
"certificado do servidor excede os límites impostos pola biblioteca de "
|
||||
"criptografía."
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1344
|
||||
#: ../js/ui/components/telepathyClient.js:1345
|
||||
msgid "Internal error"
|
||||
msgstr "Erro interno"
|
||||
|
||||
#. translators: argument is the account name, like
|
||||
#. * name@jabber.org for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1354
|
||||
#: ../js/ui/components/telepathyClient.js:1355
|
||||
#, c-format
|
||||
msgid "Unable to connect to %s"
|
||||
msgstr "Non foi posíbel conectarse a %s"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1359
|
||||
#: ../js/ui/components/telepathyClient.js:1360
|
||||
msgid "View account"
|
||||
msgstr "Ver conta"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1398
|
||||
#: ../js/ui/components/telepathyClient.js:1399
|
||||
msgid "Unknown reason"
|
||||
msgstr "Razón descoñecida"
|
||||
|
||||
@ -1030,26 +1034,26 @@ msgstr "Mostrar aplicativos"
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/dash.js:435
|
||||
#: ../js/ui/dash.js:439
|
||||
msgid "Dash"
|
||||
msgstr "Taboleiro"
|
||||
|
||||
#: ../js/ui/dateMenu.js:86
|
||||
#: ../js/ui/dateMenu.js:85
|
||||
msgid "Open Calendar"
|
||||
msgstr "Abrir Calendario"
|
||||
|
||||
#: ../js/ui/dateMenu.js:90
|
||||
#: ../js/ui/dateMenu.js:89
|
||||
msgid "Open Clocks"
|
||||
msgstr "Abrir Reloxos"
|
||||
|
||||
#: ../js/ui/dateMenu.js:97
|
||||
#: ../js/ui/dateMenu.js:96
|
||||
msgid "Date & Time Settings"
|
||||
msgstr "Preferencias de data e hora"
|
||||
|
||||
#. Translators: This is the date format to use when the calendar popup is
|
||||
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
#.
|
||||
#: ../js/ui/dateMenu.js:208
|
||||
#: ../js/ui/dateMenu.js:201
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%a, %e de %B, %Y"
|
||||
|
||||
@ -1261,17 +1265,17 @@ msgstr "Vista xeral"
|
||||
msgid "Type to search…"
|
||||
msgstr "Escriba para buscar…"
|
||||
|
||||
#: ../js/ui/panel.js:642
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Quit"
|
||||
msgstr "Saír"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:693
|
||||
#: ../js/ui/panel.js:618
|
||||
msgid "Activities"
|
||||
msgstr "Actividades"
|
||||
|
||||
#: ../js/ui/panel.js:989
|
||||
#: ../js/ui/panel.js:914
|
||||
msgid "Top Bar"
|
||||
msgstr "Barra superior"
|
||||
|
||||
@ -1280,7 +1284,7 @@ msgstr "Barra superior"
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:738
|
||||
#: ../js/ui/popupMenu.js:549
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
@ -1305,7 +1309,7 @@ msgid_plural "%d new notifications"
|
||||
msgstr[0] "%d notificación nova"
|
||||
msgstr[1] "%d notificacións novas"
|
||||
|
||||
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:807
|
||||
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759
|
||||
msgid "Lock"
|
||||
msgstr "Bloquear"
|
||||
|
||||
@ -1320,11 +1324,11 @@ msgstr "GNOME precisa bloquear a pantalla"
|
||||
#.
|
||||
#. XXX: another option is to kick the user into the gdm login
|
||||
#. screen, where we're not affected by grabs
|
||||
#: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
|
||||
#: ../js/ui/screenShield.js:775 ../js/ui/screenShield.js:1215
|
||||
msgid "Unable to lock"
|
||||
msgstr "Non foi posíbel bloquear"
|
||||
|
||||
#: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
|
||||
#: ../js/ui/screenShield.js:776 ../js/ui/screenShield.js:1216
|
||||
msgid "Lock was blocked by an application"
|
||||
msgstr "Un aplicativo impediu o bloqueo"
|
||||
|
||||
@ -1360,7 +1364,7 @@ msgstr "Contrasinal"
|
||||
msgid "Remember Password"
|
||||
msgstr "Lembrar contrasinal"
|
||||
|
||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:109
|
||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108
|
||||
msgid "Unlock"
|
||||
msgstr "Desbloquear"
|
||||
|
||||
@ -1413,9 +1417,9 @@ msgid "Large Text"
|
||||
msgstr "Texto grande"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
|
||||
#: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
|
||||
#: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
|
||||
#: ../js/ui/status/network.js:739
|
||||
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327
|
||||
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391
|
||||
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713
|
||||
msgid "Bluetooth"
|
||||
msgstr "Bluetooth"
|
||||
|
||||
@ -1436,73 +1440,82 @@ msgid "Bluetooth Settings"
|
||||
msgstr "Preferencias do Bluetooth"
|
||||
|
||||
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
|
||||
#: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:142
|
||||
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140
|
||||
msgid "hardware disabled"
|
||||
msgstr "hardware desactivado"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:197
|
||||
#: ../js/ui/status/bluetooth.js:198
|
||||
msgid "Connection"
|
||||
msgstr "Conexión"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:404
|
||||
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399
|
||||
msgid "disconnecting..."
|
||||
msgstr "desconectando…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:410
|
||||
#: ../js/ui/status/network.js:1343
|
||||
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405
|
||||
#: ../js/ui/status/network.js:1298
|
||||
msgid "connecting..."
|
||||
msgstr "conectando…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:239
|
||||
#: ../js/ui/status/bluetooth.js:240
|
||||
msgid "Send Files…"
|
||||
msgstr "Enviar ficheiros…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:246
|
||||
#: ../js/ui/status/bluetooth.js:247
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Preferencias do teclado"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:249
|
||||
#: ../js/ui/status/bluetooth.js:250
|
||||
msgid "Mouse Settings"
|
||||
msgstr "Preferencias do rato"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
|
||||
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313
|
||||
msgid "Sound Settings"
|
||||
msgstr "Preferencias do son"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:322
|
||||
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356
|
||||
#, c-format
|
||||
msgid "Authorization request from %s"
|
||||
msgstr "Solicitude de autorización de %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:328
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "O dispositivo %s quere acceder ao servizo «%s»"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:330
|
||||
msgid "Always grant access"
|
||||
msgstr "Conceder acceso sempre"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:331
|
||||
msgid "Grant this time only"
|
||||
msgstr "Conceder só esta vez"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:332
|
||||
msgid "Reject"
|
||||
msgstr "Rexeitar"
|
||||
|
||||
#. Translators: argument is the device short name
|
||||
#: ../js/ui/status/bluetooth.js:359
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Confirmación de emparellado para «%s»"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
|
||||
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399
|
||||
#: ../js/ui/status/bluetooth.js:430
|
||||
#, c-format
|
||||
msgid "Device %s wants to pair with this computer"
|
||||
msgstr "O dispositivo «%s» quere emparellarse con este equipo"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:336
|
||||
msgid "Allow"
|
||||
msgstr "Permitir"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:337
|
||||
msgid "Deny"
|
||||
msgstr "Denegar"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:362
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "O dispositivo %s quere acceder ao servizo «%s»"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:364
|
||||
msgid "Always grant access"
|
||||
msgstr "Conceder acceso sempre"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:365
|
||||
msgid "Grant this time only"
|
||||
msgstr "Conceder só esta vez"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:366
|
||||
msgid "Reject"
|
||||
msgstr "Rexeitar"
|
||||
|
||||
#. Translators: argument is the device short name
|
||||
#: ../js/ui/status/bluetooth.js:393
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Confirmación de emparellado para «%s»"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:400
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Please confirm whether the Passkey '%06d' matches the one on the device."
|
||||
@ -1510,24 +1523,24 @@ msgstr ""
|
||||
"Confirme que a frase de paso «%06d» coincide coa mostrada no dispositivo."
|
||||
|
||||
#. Translators: this is the verb, not the noun
|
||||
#: ../js/ui/status/bluetooth.js:369
|
||||
#: ../js/ui/status/bluetooth.js:403
|
||||
msgid "Matches"
|
||||
msgstr "Coincide"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:370
|
||||
#: ../js/ui/status/bluetooth.js:404
|
||||
msgid "Does not match"
|
||||
msgstr "Non coincide"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:389
|
||||
#: ../js/ui/status/bluetooth.js:423
|
||||
#, c-format
|
||||
msgid "Pairing request for %s"
|
||||
msgstr "Solicitude de emparellamento para «%s»"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:397
|
||||
#: ../js/ui/status/bluetooth.js:431
|
||||
msgid "Please enter the PIN mentioned on the device."
|
||||
msgstr "Escriba o PIN mencionado no dispositivo."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:414
|
||||
#: ../js/ui/status/bluetooth.js:448
|
||||
msgid "OK"
|
||||
msgstr "Aceptar"
|
||||
|
||||
@ -1547,87 +1560,81 @@ msgstr "Volume, rede, batería"
|
||||
msgid "<unknown>"
|
||||
msgstr "<descoñecido>"
|
||||
|
||||
#: ../js/ui/status/network.js:127
|
||||
#: ../js/ui/status/network.js:125
|
||||
msgid "Wi-Fi"
|
||||
msgstr "Wifi"
|
||||
|
||||
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
|
||||
#: ../js/ui/status/network.js:164
|
||||
#: ../js/ui/status/network.js:162
|
||||
msgid "disabled"
|
||||
msgstr "desactivada"
|
||||
|
||||
#. Translators: this is for network devices that are physically present but are not
|
||||
#. under NetworkManager's control (and thus cannot be used in the menu)
|
||||
#: ../js/ui/status/network.js:402
|
||||
#: ../js/ui/status/network.js:397
|
||||
msgid "unmanaged"
|
||||
msgstr "non xestionada"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:413 ../js/ui/status/network.js:1346
|
||||
#: ../js/ui/status/network.js:408 ../js/ui/status/network.js:1301
|
||||
msgid "authentication required"
|
||||
msgstr "requírese autenticación"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
#: ../js/ui/status/network.js:423
|
||||
#: ../js/ui/status/network.js:419
|
||||
msgid "firmware missing"
|
||||
msgstr "falta o «firmware»"
|
||||
|
||||
#. Translators: this is for wired network devices that are physically disconnected
|
||||
#: ../js/ui/status/network.js:430
|
||||
msgid "cable unplugged"
|
||||
msgstr "cable desconectado"
|
||||
|
||||
#. Translators: this is for a network device that cannot be activated (for example it
|
||||
#. is disabled by rfkill, or it has no coverage
|
||||
#: ../js/ui/status/network.js:435
|
||||
#: ../js/ui/status/network.js:423
|
||||
msgid "unavailable"
|
||||
msgstr "non dispoñíbel"
|
||||
|
||||
#: ../js/ui/status/network.js:437 ../js/ui/status/network.js:1348
|
||||
#: ../js/ui/status/network.js:425 ../js/ui/status/network.js:1303
|
||||
msgid "connection failed"
|
||||
msgstr "conexión fallada"
|
||||
|
||||
#: ../js/ui/status/network.js:490 ../js/ui/status/network.js:1236
|
||||
#: ../js/ui/status/network.js:1424
|
||||
#: ../js/ui/status/network.js:478 ../js/ui/status/network.js:1190
|
||||
msgid "More…"
|
||||
msgstr "Máis…"
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
#: ../js/ui/status/network.js:518 ../js/ui/status/network.js:1191
|
||||
#: ../js/ui/status/network.js:506 ../js/ui/status/network.js:1142
|
||||
msgid "Connected (private)"
|
||||
msgstr "Conectada (privada)"
|
||||
|
||||
#: ../js/ui/status/network.js:597
|
||||
#: ../js/ui/status/network.js:572
|
||||
msgid "Wired"
|
||||
msgstr "Con fíos"
|
||||
|
||||
#: ../js/ui/status/network.js:611
|
||||
#: ../js/ui/status/network.js:592
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Banda larga móbil"
|
||||
|
||||
#: ../js/ui/status/network.js:1522
|
||||
#: ../js/ui/status/network.js:1474
|
||||
msgid "Enable networking"
|
||||
msgstr "Activar rede"
|
||||
|
||||
#: ../js/ui/status/network.js:1583
|
||||
#: ../js/ui/status/network.js:1522
|
||||
msgid "Network Settings"
|
||||
msgstr "Preferencias da rede"
|
||||
|
||||
#: ../js/ui/status/network.js:1600
|
||||
#: ../js/ui/status/network.js:1539
|
||||
msgid "Network Manager"
|
||||
msgstr "Xestor da rede"
|
||||
|
||||
#: ../js/ui/status/network.js:1690
|
||||
#: ../js/ui/status/network.js:1623
|
||||
msgid "Connection failed"
|
||||
msgstr "Produciuse un fallo na conexión"
|
||||
|
||||
#: ../js/ui/status/network.js:1691
|
||||
#: ../js/ui/status/network.js:1624
|
||||
msgid "Activation of network connection failed"
|
||||
msgstr "Produciuse un fallo na activación da conexión de rede"
|
||||
|
||||
#: ../js/ui/status/network.js:2047
|
||||
#: ../js/ui/status/network.js:1937
|
||||
msgid "Networking is disabled"
|
||||
msgstr "A rede está desactivada"
|
||||
|
||||
@ -1728,72 +1735,72 @@ msgctxt "device"
|
||||
msgid "Unknown"
|
||||
msgstr "Descoñecido"
|
||||
|
||||
#: ../js/ui/status/volume.js:124
|
||||
#: ../js/ui/status/volume.js:121
|
||||
msgid "Volume changed"
|
||||
msgstr "Volume cambiado"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
#: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
|
||||
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294
|
||||
msgid "Volume"
|
||||
msgstr "Volume"
|
||||
|
||||
#: ../js/ui/status/volume.js:258
|
||||
#: ../js/ui/status/volume.js:255
|
||||
msgid "Microphone"
|
||||
msgstr "Micrófono"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:120
|
||||
#: ../js/ui/unlockDialog.js:119
|
||||
msgid "Log in as another user"
|
||||
msgstr "Iniciar sesión como outro usuario"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:141
|
||||
#: ../js/ui/unlockDialog.js:140
|
||||
msgid "Unlock Window"
|
||||
msgstr "Desbloquear xanela"
|
||||
|
||||
#: ../js/ui/userMenu.js:193
|
||||
#: ../js/ui/userMenu.js:149
|
||||
msgid "Available"
|
||||
msgstr "Dispoñíbel"
|
||||
|
||||
#: ../js/ui/userMenu.js:196
|
||||
#: ../js/ui/userMenu.js:152
|
||||
msgid "Busy"
|
||||
msgstr "Ocupado"
|
||||
|
||||
#: ../js/ui/userMenu.js:199
|
||||
#: ../js/ui/userMenu.js:155
|
||||
msgid "Invisible"
|
||||
msgstr "Invisíbel"
|
||||
|
||||
#: ../js/ui/userMenu.js:202
|
||||
#: ../js/ui/userMenu.js:158
|
||||
msgid "Away"
|
||||
msgstr "Ausente"
|
||||
|
||||
#: ../js/ui/userMenu.js:205
|
||||
#: ../js/ui/userMenu.js:161
|
||||
msgid "Idle"
|
||||
msgstr "Inactivo"
|
||||
|
||||
#: ../js/ui/userMenu.js:208
|
||||
#: ../js/ui/userMenu.js:164
|
||||
msgid "Offline"
|
||||
msgstr "Desconectado"
|
||||
|
||||
#: ../js/ui/userMenu.js:781
|
||||
#: ../js/ui/userMenu.js:736
|
||||
msgid "Notifications"
|
||||
msgstr "Notificacións"
|
||||
|
||||
#: ../js/ui/userMenu.js:797
|
||||
#: ../js/ui/userMenu.js:749
|
||||
msgid "Switch User"
|
||||
msgstr "Cambiar de usuario"
|
||||
|
||||
#: ../js/ui/userMenu.js:802
|
||||
#: ../js/ui/userMenu.js:754
|
||||
msgid "Log Out"
|
||||
msgstr "Saír da sesión"
|
||||
|
||||
#: ../js/ui/userMenu.js:822
|
||||
#: ../js/ui/userMenu.js:774
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "Instalar actualizacións e reiniciar"
|
||||
|
||||
#: ../js/ui/userMenu.js:840
|
||||
#: ../js/ui/userMenu.js:792
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "O seu estado de conversa estabelecerase a «ocupado»"
|
||||
|
||||
#: ../js/ui/userMenu.js:841
|
||||
#: ../js/ui/userMenu.js:793
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
@ -1802,22 +1809,22 @@ msgstr ""
|
||||
"conversa. O seu estado de conexión axustouse para que outros saiban que non "
|
||||
"quere ver as súas mensaxes."
|
||||
|
||||
#: ../js/ui/userMenu.js:888
|
||||
#: ../js/ui/userMenu.js:834
|
||||
msgid "Other users are logged in."
|
||||
msgstr "Hai outros usuarios conectados."
|
||||
|
||||
#: ../js/ui/userMenu.js:893
|
||||
#: ../js/ui/userMenu.js:839
|
||||
msgid "Shutting down might cause them to lose unsaved work."
|
||||
msgstr "Se apaga o computador pode perder o traballo que non gardou."
|
||||
|
||||
#. Translators: Remote here refers to a remote session, like a ssh login
|
||||
#: ../js/ui/userMenu.js:921
|
||||
#: ../js/ui/userMenu.js:867
|
||||
#, c-format
|
||||
msgid "%s (remote)"
|
||||
msgstr "%s (remoto)"
|
||||
|
||||
#. Translators: Console here refers to a tty like a VT console
|
||||
#: ../js/ui/userMenu.js:924
|
||||
#: ../js/ui/userMenu.js:870
|
||||
#, c-format
|
||||
msgid "%s (console)"
|
||||
msgstr "%s (consola)"
|
||||
@ -1875,21 +1882,21 @@ msgstr[1] "%u entradas"
|
||||
msgid "System Sounds"
|
||||
msgstr "Sons do sistema"
|
||||
|
||||
#: ../src/main.c:372
|
||||
#: ../src/main.c:353
|
||||
msgid "Print version"
|
||||
msgstr "Imprimir versión"
|
||||
|
||||
#: ../src/main.c:378
|
||||
#: ../src/main.c:359
|
||||
msgid "Mode used by GDM for login screen"
|
||||
msgstr "Modo usado por GDM para a pantalla de inicio"
|
||||
|
||||
#: ../src/main.c:384
|
||||
#: ../src/main.c:365
|
||||
msgid "Use a specific mode, e.g. \"gdm\" for login screen"
|
||||
msgstr ""
|
||||
"Usar un modo específico, por exemplo, «gdm» para a pantalla de inicio de "
|
||||
"sesión"
|
||||
|
||||
#: ../src/main.c:390
|
||||
#: ../src/main.c:371
|
||||
msgid "List possible modes"
|
||||
msgstr "Listar os modos posíbeis"
|
||||
|
||||
@ -1910,6 +1917,9 @@ msgstr "O contrasinal non pode estar baleiro"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "O usuario rexeitou o diálogo de autenticación"
|
||||
|
||||
#~ msgid "cable unplugged"
|
||||
#~ msgstr "cable desconectado"
|
||||
|
||||
#~ msgid "Whether to collect stats about applications usage"
|
||||
#~ msgstr ""
|
||||
#~ "Indica se se deben recolectar estatísticas sobre o uso dos aplicativos"
|
||||
@ -2027,9 +2037,6 @@ msgstr "O usuario rexeitou o diálogo de autenticación"
|
||||
#~ msgid "System Settings"
|
||||
#~ msgstr "Preferencias do sistema"
|
||||
|
||||
#~ msgid "Switch Session"
|
||||
#~ msgstr "Cambiar de sesión"
|
||||
|
||||
#~ msgid "disabled OpenSearch providers"
|
||||
#~ msgstr "fornecedores OpenSearch desactivados"
|
||||
|
||||
|
379
po/nb.po
379
po/nb.po
@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell 3.9.x\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-05-28 09:43+0200\n"
|
||||
"PO-Revision-Date: 2013-05-28 09:44+0200\n"
|
||||
"POT-Creation-Date: 2013-07-18 15:29+0200\n"
|
||||
"PO-Revision-Date: 2013-07-04 11:09+0200\n"
|
||||
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
|
||||
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
|
||||
"Language: \n"
|
||||
@ -331,37 +331,41 @@ msgstr "Utvidelse"
|
||||
msgid "Select an extension to configure using the combobox above."
|
||||
msgstr "Velg en utvidelse som skal konfigureres med komboboksen over."
|
||||
|
||||
#: ../js/gdm/loginDialog.js:371
|
||||
msgid "Session…"
|
||||
msgstr "Økt …"
|
||||
#: ../js/gdm/loginDialog.js:308
|
||||
msgid "Choose Session"
|
||||
msgstr "Velg økt"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:326
|
||||
msgid "Session"
|
||||
msgstr "Økt"
|
||||
|
||||
#. translators: this message is shown below the user list on the
|
||||
#. login screen. It can be activated to reveal an entry for
|
||||
#. manually entering the username.
|
||||
#: ../js/gdm/loginDialog.js:601
|
||||
#: ../js/gdm/loginDialog.js:533
|
||||
msgid "Not listed?"
|
||||
msgstr "Ikke listet?"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:776 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
|
||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:96
|
||||
#: ../js/ui/userMenu.js:938
|
||||
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
|
||||
#: ../js/ui/userMenu.js:884
|
||||
msgid "Cancel"
|
||||
msgstr "Avbryt"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:791
|
||||
#: ../js/gdm/loginDialog.js:833
|
||||
msgctxt "button"
|
||||
msgid "Sign In"
|
||||
msgstr "Logg inn"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:791
|
||||
#: ../js/gdm/loginDialog.js:833
|
||||
msgid "Next"
|
||||
msgstr "Neste"
|
||||
|
||||
#. Translators: this message is shown below the username entry field
|
||||
#. to clue the user in on how to login to the local network realm
|
||||
#: ../js/gdm/loginDialog.js:888
|
||||
#: ../js/gdm/loginDialog.js:934
|
||||
#, c-format
|
||||
msgid "(e.g., user or %s)"
|
||||
msgstr "(f.eks. bruker eller %s)"
|
||||
@ -369,12 +373,12 @@ msgstr "(f.eks. bruker eller %s)"
|
||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||
#. is not visible here since we only care about phase2 authentication
|
||||
#. (and don't even care of which one)
|
||||
#: ../js/gdm/loginDialog.js:892 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/ui/components/networkAgent.js:278
|
||||
msgid "Username: "
|
||||
msgstr "Brukernavn: "
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1158
|
||||
#: ../js/gdm/loginDialog.js:1205
|
||||
msgid "Login Window"
|
||||
msgstr "Innloggingsvindu"
|
||||
|
||||
@ -383,8 +387,8 @@ msgstr "Innloggingsvindu"
|
||||
msgid "Power"
|
||||
msgstr "Strøm"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
|
||||
#: ../js/ui/userMenu.js:816
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655
|
||||
#: ../js/ui/userMenu.js:768
|
||||
msgid "Suspend"
|
||||
msgstr "Hvilemodus"
|
||||
|
||||
@ -392,8 +396,8 @@ msgstr "Hvilemodus"
|
||||
msgid "Restart"
|
||||
msgstr "Start på nytt"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
|
||||
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653
|
||||
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888
|
||||
msgid "Power Off"
|
||||
msgstr "Slå av"
|
||||
|
||||
@ -422,23 +426,23 @@ msgstr "Klarte ikke å lese kommando:"
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Kjøring av «%s» feilet:"
|
||||
|
||||
#: ../js/ui/appDisplay.js:361
|
||||
#: ../js/ui/appDisplay.js:397
|
||||
msgid "Frequent"
|
||||
msgstr "Ofte"
|
||||
|
||||
#: ../js/ui/appDisplay.js:368
|
||||
#: ../js/ui/appDisplay.js:404
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: ../js/ui/appDisplay.js:960
|
||||
#: ../js/ui/appDisplay.js:996
|
||||
msgid "New Window"
|
||||
msgstr "Nytt vindu"
|
||||
|
||||
#: ../js/ui/appDisplay.js:963 ../js/ui/dash.js:284
|
||||
#: ../js/ui/appDisplay.js:999 ../js/ui/dash.js:284
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Fjern fra favoritter"
|
||||
|
||||
#: ../js/ui/appDisplay.js:964
|
||||
#: ../js/ui/appDisplay.js:1000
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Legg til i favoritter"
|
||||
|
||||
@ -452,7 +456,7 @@ msgstr "%s ble lagt til i dine favoritter."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s ble fjernet fra dine favoritter."
|
||||
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744
|
||||
msgid "Settings"
|
||||
msgstr "Innstillinger"
|
||||
|
||||
@ -577,35 +581,35 @@ msgid "S"
|
||||
msgstr "Lø"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:735
|
||||
#: ../js/ui/calendar.js:750
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Ingenting planlagt"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:768
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A %B %d"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:754
|
||||
#: ../js/ui/calendar.js:771
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A %B %d, %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:764
|
||||
#: ../js/ui/calendar.js:782
|
||||
msgid "Today"
|
||||
msgstr "I dag"
|
||||
|
||||
#: ../js/ui/calendar.js:768
|
||||
#: ../js/ui/calendar.js:786
|
||||
msgid "Tomorrow"
|
||||
msgstr "I morgen"
|
||||
|
||||
#: ../js/ui/calendar.js:779
|
||||
#: ../js/ui/calendar.js:797
|
||||
msgid "This week"
|
||||
msgstr "Denne uken"
|
||||
|
||||
#: ../js/ui/calendar.js:787
|
||||
#: ../js/ui/calendar.js:805
|
||||
msgid "Next week"
|
||||
msgstr "Neste uke"
|
||||
|
||||
@ -794,14 +798,14 @@ msgstr "<b>%d</b> <b>%B</b> <b>%Y</b>, <b>%H.%M</b> "
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/components/telepathyClient.js:985
|
||||
#: ../js/ui/components/telepathyClient.js:986
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "%s er nå kjent som %s"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1088
|
||||
#: ../js/ui/components/telepathyClient.js:1089
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "Invitasjon til %s"
|
||||
@ -809,38 +813,38 @@ msgstr "Invitasjon til %s"
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1096
|
||||
#: ../js/ui/components/telepathyClient.js:1097
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "%s inviterer deg til å bli med i %s"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1098
|
||||
#: ../js/ui/components/telepathyClient.js:1137
|
||||
#: ../js/ui/components/telepathyClient.js:1177
|
||||
#: ../js/ui/components/telepathyClient.js:1240
|
||||
#: ../js/ui/components/telepathyClient.js:1099
|
||||
#: ../js/ui/components/telepathyClient.js:1138
|
||||
#: ../js/ui/components/telepathyClient.js:1178
|
||||
#: ../js/ui/components/telepathyClient.js:1241
|
||||
msgid "Decline"
|
||||
msgstr "Avslå"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1099
|
||||
#: ../js/ui/components/telepathyClient.js:1178
|
||||
#: ../js/ui/components/telepathyClient.js:1241
|
||||
#: ../js/ui/components/telepathyClient.js:1100
|
||||
#: ../js/ui/components/telepathyClient.js:1179
|
||||
#: ../js/ui/components/telepathyClient.js:1242
|
||||
msgid "Accept"
|
||||
msgstr "Godta"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1129
|
||||
#: ../js/ui/components/telepathyClient.js:1130
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "Videosamtale fra %s"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1132
|
||||
#: ../js/ui/components/telepathyClient.js:1133
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "Samtale fra %s"
|
||||
|
||||
#. translators: this is a button label (verb), not a noun
|
||||
#: ../js/ui/components/telepathyClient.js:1139
|
||||
#: ../js/ui/components/telepathyClient.js:1140
|
||||
msgid "Answer"
|
||||
msgstr "Svar"
|
||||
|
||||
@ -849,110 +853,110 @@ msgstr "Svar"
|
||||
#. * file name. The string will be something
|
||||
#. * like: "Alice is sending you test.ogg"
|
||||
#.
|
||||
#: ../js/ui/components/telepathyClient.js:1171
|
||||
#: ../js/ui/components/telepathyClient.js:1172
|
||||
#, c-format
|
||||
msgid "%s is sending you %s"
|
||||
msgstr "%s sender deg %s"
|
||||
|
||||
#. To translators: The parameter is the contact's alias
|
||||
#: ../js/ui/components/telepathyClient.js:1206
|
||||
#: ../js/ui/components/telepathyClient.js:1207
|
||||
#, c-format
|
||||
msgid "%s would like permission to see when you are online"
|
||||
msgstr "%s vil ha rettigheter til å se når du er tilkoblet"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1298
|
||||
#: ../js/ui/components/telepathyClient.js:1299
|
||||
msgid "Network error"
|
||||
msgstr "Nettverksfeil"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1300
|
||||
#: ../js/ui/components/telepathyClient.js:1301
|
||||
msgid "Authentication failed"
|
||||
msgstr "Autentisering feilet"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1302
|
||||
#: ../js/ui/components/telepathyClient.js:1303
|
||||
msgid "Encryption error"
|
||||
msgstr "Feil ved kryptering"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1304
|
||||
#: ../js/ui/components/telepathyClient.js:1305
|
||||
msgid "Certificate not provided"
|
||||
msgstr "Sertifikat ikke oppgitt"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1306
|
||||
#: ../js/ui/components/telepathyClient.js:1307
|
||||
msgid "Certificate untrusted"
|
||||
msgstr "Stoler ikke på sertifikatet"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1308
|
||||
#: ../js/ui/components/telepathyClient.js:1309
|
||||
msgid "Certificate expired"
|
||||
msgstr "Sertifikatet er utløpt"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1310
|
||||
#: ../js/ui/components/telepathyClient.js:1311
|
||||
msgid "Certificate not activated"
|
||||
msgstr "Sertifikatet er ikke aktivert"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1312
|
||||
#: ../js/ui/components/telepathyClient.js:1313
|
||||
msgid "Certificate hostname mismatch"
|
||||
msgstr "Feil vertsnavn for sertifikat"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1314
|
||||
#: ../js/ui/components/telepathyClient.js:1315
|
||||
msgid "Certificate fingerprint mismatch"
|
||||
msgstr "Feil fingeravtrykk for sertifikat"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1316
|
||||
#: ../js/ui/components/telepathyClient.js:1317
|
||||
msgid "Certificate self-signed"
|
||||
msgstr "Sertifikatet er selvsignert"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1318
|
||||
#: ../js/ui/components/telepathyClient.js:1319
|
||||
msgid "Status is set to offline"
|
||||
msgstr "Status er satt til frakoblet"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1320
|
||||
#: ../js/ui/components/telepathyClient.js:1321
|
||||
msgid "Encryption is not available"
|
||||
msgstr "Kryptering er ikke tilgjengelig"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1322
|
||||
#: ../js/ui/components/telepathyClient.js:1323
|
||||
msgid "Certificate is invalid"
|
||||
msgstr "Sertifikatet er ugyldig"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1324
|
||||
#: ../js/ui/components/telepathyClient.js:1325
|
||||
msgid "Connection has been refused"
|
||||
msgstr "Tilkobling ble nektet"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1326
|
||||
#: ../js/ui/components/telepathyClient.js:1327
|
||||
msgid "Connection can't be established"
|
||||
msgstr "Tilkobling kan ikke etableres"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1328
|
||||
#: ../js/ui/components/telepathyClient.js:1329
|
||||
msgid "Connection has been lost"
|
||||
msgstr "Tilkobling tapt"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1330
|
||||
#: ../js/ui/components/telepathyClient.js:1331
|
||||
msgid "This account is already connected to the server"
|
||||
msgstr "Denne kontoen er allerede koblet til tjeneren"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1332
|
||||
#: ../js/ui/components/telepathyClient.js:1333
|
||||
msgid ""
|
||||
"Connection has been replaced by a new connection using the same resource"
|
||||
msgstr ""
|
||||
"Tilkoblingen har blitt erstattet av en ny tilkobling som bruker samme ressurs"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1334
|
||||
#: ../js/ui/components/telepathyClient.js:1335
|
||||
msgid "The account already exists on the server"
|
||||
msgstr "Kontoen eksisterer allerede på tjeneren"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1336
|
||||
#: ../js/ui/components/telepathyClient.js:1337
|
||||
msgid "Server is currently too busy to handle the connection"
|
||||
msgstr "Tjener er for opptatt til å håndtere tilkoblingen"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1338
|
||||
#: ../js/ui/components/telepathyClient.js:1339
|
||||
msgid "Certificate has been revoked"
|
||||
msgstr "Sertifikatet er tilbaketrukket"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1340
|
||||
#: ../js/ui/components/telepathyClient.js:1341
|
||||
msgid ""
|
||||
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
|
||||
msgstr ""
|
||||
"Sertifikatet bruker en usikker sifferalgoritme eller er krytografisk svakt"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1342
|
||||
#: ../js/ui/components/telepathyClient.js:1343
|
||||
msgid ""
|
||||
"The length of the server certificate, or the depth of the server certificate "
|
||||
"chain, exceed the limits imposed by the cryptography library"
|
||||
@ -960,22 +964,22 @@ msgstr ""
|
||||
"Lengden eller dybden på tjenersertifikatet oversteg grensen som er satt i "
|
||||
"kryptografibiblioteket"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1344
|
||||
#: ../js/ui/components/telepathyClient.js:1345
|
||||
msgid "Internal error"
|
||||
msgstr "Intern feil"
|
||||
|
||||
#. translators: argument is the account name, like
|
||||
#. * name@jabber.org for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1354
|
||||
#: ../js/ui/components/telepathyClient.js:1355
|
||||
#, c-format
|
||||
msgid "Unable to connect to %s"
|
||||
msgstr "Kan ikke koble til %s"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1359
|
||||
#: ../js/ui/components/telepathyClient.js:1360
|
||||
msgid "View account"
|
||||
msgstr "Vis konto"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1398
|
||||
#: ../js/ui/components/telepathyClient.js:1399
|
||||
msgid "Unknown reason"
|
||||
msgstr "Ukjent årsak"
|
||||
|
||||
@ -989,26 +993,26 @@ msgstr "Vis programmer"
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/dash.js:435
|
||||
#: ../js/ui/dash.js:439
|
||||
msgid "Dash"
|
||||
msgstr "Favoritter"
|
||||
|
||||
#: ../js/ui/dateMenu.js:86
|
||||
#: ../js/ui/dateMenu.js:85
|
||||
msgid "Open Calendar"
|
||||
msgstr "Åpne kalender"
|
||||
|
||||
#: ../js/ui/dateMenu.js:90
|
||||
#: ../js/ui/dateMenu.js:89
|
||||
msgid "Open Clocks"
|
||||
msgstr "Åpne Klokker"
|
||||
|
||||
#: ../js/ui/dateMenu.js:97
|
||||
#: ../js/ui/dateMenu.js:96
|
||||
msgid "Date & Time Settings"
|
||||
msgstr "Innstillinger for dato og klokkeslett"
|
||||
|
||||
#. Translators: This is the date format to use when the calendar popup is
|
||||
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
#.
|
||||
#: ../js/ui/dateMenu.js:208
|
||||
#: ../js/ui/dateMenu.js:201
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%a %e %B, %Y"
|
||||
|
||||
@ -1166,31 +1170,31 @@ msgstr "Vis kildekode"
|
||||
msgid "Web Page"
|
||||
msgstr "Nettside"
|
||||
|
||||
#: ../js/ui/messageTray.js:1182
|
||||
#: ../js/ui/messageTray.js:1241
|
||||
msgid "Open"
|
||||
msgstr "Åpne"
|
||||
|
||||
#: ../js/ui/messageTray.js:1189
|
||||
#: ../js/ui/messageTray.js:1248
|
||||
msgid "Remove"
|
||||
msgstr "Fjern"
|
||||
|
||||
#: ../js/ui/messageTray.js:1501
|
||||
#: ../js/ui/messageTray.js:1560
|
||||
msgid "Clear Messages"
|
||||
msgstr "Tøm meldinger"
|
||||
|
||||
#: ../js/ui/messageTray.js:1528
|
||||
#: ../js/ui/messageTray.js:1587
|
||||
msgid "Notification Settings"
|
||||
msgstr "Innstillinger for varsling"
|
||||
|
||||
#: ../js/ui/messageTray.js:1707
|
||||
#: ../js/ui/messageTray.js:1770
|
||||
msgid "No Messages"
|
||||
msgstr "Ingen meldinger"
|
||||
|
||||
#: ../js/ui/messageTray.js:1780
|
||||
#: ../js/ui/messageTray.js:1842
|
||||
msgid "Message Tray"
|
||||
msgstr "Meldingstrau"
|
||||
|
||||
#: ../js/ui/messageTray.js:2805
|
||||
#: ../js/ui/messageTray.js:2854
|
||||
msgid "System Information"
|
||||
msgstr "Systeminformasjon"
|
||||
|
||||
@ -1199,7 +1203,7 @@ msgctxt "program"
|
||||
msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
|
||||
#: ../js/ui/overviewControls.js:472 ../js/ui/screenShield.js:150
|
||||
#: ../js/ui/overviewControls.js:474 ../js/ui/screenShield.js:150
|
||||
#, c-format
|
||||
msgid "%d new message"
|
||||
msgid_plural "%d new messages"
|
||||
@ -1222,17 +1226,17 @@ msgstr "Oversikt"
|
||||
msgid "Type to search…"
|
||||
msgstr "Skriv for å søke …"
|
||||
|
||||
#: ../js/ui/panel.js:642
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Quit"
|
||||
msgstr "Avslutt"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:693
|
||||
#: ../js/ui/panel.js:618
|
||||
msgid "Activities"
|
||||
msgstr "Aktiviteter"
|
||||
|
||||
#: ../js/ui/panel.js:989
|
||||
#: ../js/ui/panel.js:914
|
||||
msgid "Top Bar"
|
||||
msgstr "Topp-panel"
|
||||
|
||||
@ -1241,7 +1245,7 @@ msgstr "Topp-panel"
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:738
|
||||
#: ../js/ui/popupMenu.js:517
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
@ -1266,7 +1270,7 @@ msgid_plural "%d new notifications"
|
||||
msgstr[0] "%d ny varsling"
|
||||
msgstr[1] "%d nye varslinger"
|
||||
|
||||
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:807
|
||||
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759
|
||||
msgid "Lock"
|
||||
msgstr "Lås"
|
||||
|
||||
@ -1281,19 +1285,19 @@ msgstr "GNOME må låse skjermen"
|
||||
#.
|
||||
#. XXX: another option is to kick the user into the gdm login
|
||||
#. screen, where we're not affected by grabs
|
||||
#: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
|
||||
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1216
|
||||
msgid "Unable to lock"
|
||||
msgstr "Kan ikke låse"
|
||||
|
||||
#: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
|
||||
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1217
|
||||
msgid "Lock was blocked by an application"
|
||||
msgstr "Låsing ble stoppet av et program"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:453
|
||||
#: ../js/ui/searchDisplay.js:445
|
||||
msgid "Searching…"
|
||||
msgstr "Søker …"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:497
|
||||
#: ../js/ui/searchDisplay.js:489
|
||||
msgid "No results."
|
||||
msgstr "Ingen resultater."
|
||||
|
||||
@ -1321,7 +1325,7 @@ msgstr "Passord"
|
||||
msgid "Remember Password"
|
||||
msgstr "Husk passord"
|
||||
|
||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:109
|
||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108
|
||||
msgid "Unlock"
|
||||
msgstr "Lås opp"
|
||||
|
||||
@ -1374,9 +1378,9 @@ msgid "Large Text"
|
||||
msgstr "Stor tekst"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
|
||||
#: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
|
||||
#: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
|
||||
#: ../js/ui/status/network.js:739
|
||||
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327
|
||||
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391
|
||||
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713
|
||||
msgid "Bluetooth"
|
||||
msgstr "Bluetooth"
|
||||
|
||||
@ -1397,97 +1401,106 @@ msgid "Bluetooth Settings"
|
||||
msgstr "Innstillinger for Bluetooth"
|
||||
|
||||
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
|
||||
#: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:142
|
||||
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140
|
||||
msgid "hardware disabled"
|
||||
msgstr "maskinvare slått av"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:197
|
||||
#: ../js/ui/status/bluetooth.js:198
|
||||
msgid "Connection"
|
||||
msgstr "Tilkobling"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:404
|
||||
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399
|
||||
msgid "disconnecting..."
|
||||
msgstr "kobler fra …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:410
|
||||
#: ../js/ui/status/network.js:1343
|
||||
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405
|
||||
#: ../js/ui/status/network.js:1298
|
||||
msgid "connecting..."
|
||||
msgstr "kobler til …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:239
|
||||
#: ../js/ui/status/bluetooth.js:240
|
||||
msgid "Send Files…"
|
||||
msgstr "Send filer …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:246
|
||||
#: ../js/ui/status/bluetooth.js:247
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Innstillinger for tastatur"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:249
|
||||
#: ../js/ui/status/bluetooth.js:250
|
||||
msgid "Mouse Settings"
|
||||
msgstr "Innstillinger for mus"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
|
||||
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313
|
||||
msgid "Sound Settings"
|
||||
msgstr "Innstillinger for lyd"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:322
|
||||
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356
|
||||
#, c-format
|
||||
msgid "Authorization request from %s"
|
||||
msgstr "Forespørsel om autorisering fra %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:328
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "Enhet %s vil ha tilgang til tjenesten «%s»"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:330
|
||||
msgid "Always grant access"
|
||||
msgstr "Alltid gi tilgang"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:331
|
||||
msgid "Grant this time only"
|
||||
msgstr "Gi tilgang kun denne ene gangen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:332
|
||||
msgid "Reject"
|
||||
msgstr "Avvis"
|
||||
|
||||
#. Translators: argument is the device short name
|
||||
#: ../js/ui/status/bluetooth.js:359
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Bekreftelse for tilkobling for %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
|
||||
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399
|
||||
#: ../js/ui/status/bluetooth.js:430
|
||||
#, c-format
|
||||
msgid "Device %s wants to pair with this computer"
|
||||
msgstr "Enhet %s vil koble seg sammen med denne datamaskinen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:336
|
||||
msgid "Allow"
|
||||
msgstr "Tillat"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:337
|
||||
msgid "Deny"
|
||||
msgstr "Nekt"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:362
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "Enhet %s vil ha tilgang til tjenesten «%s»"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:364
|
||||
msgid "Always grant access"
|
||||
msgstr "Alltid gi tilgang"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:365
|
||||
msgid "Grant this time only"
|
||||
msgstr "Gi tilgang kun denne ene gangen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:366
|
||||
msgid "Reject"
|
||||
msgstr "Avvis"
|
||||
|
||||
#. Translators: argument is the device short name
|
||||
#: ../js/ui/status/bluetooth.js:393
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Bekreftelse for tilkobling for %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:400
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Please confirm whether the Passkey '%06d' matches the one on the device."
|
||||
msgstr "Vennligst bekreft om passord «%06d» er lik den som brukes på enheten."
|
||||
|
||||
#. Translators: this is the verb, not the noun
|
||||
#: ../js/ui/status/bluetooth.js:369
|
||||
#: ../js/ui/status/bluetooth.js:403
|
||||
msgid "Matches"
|
||||
msgstr "Stemmer overens"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:370
|
||||
#: ../js/ui/status/bluetooth.js:404
|
||||
msgid "Does not match"
|
||||
msgstr "Stemmer ikke overens"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:389
|
||||
#: ../js/ui/status/bluetooth.js:423
|
||||
#, c-format
|
||||
msgid "Pairing request for %s"
|
||||
msgstr "Forespørsel om tilkobling for %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:397
|
||||
#: ../js/ui/status/bluetooth.js:431
|
||||
msgid "Please enter the PIN mentioned on the device."
|
||||
msgstr "Vennligst oppgi PIN som oppgitt på enheten."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:414
|
||||
#: ../js/ui/status/bluetooth.js:448
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
@ -1507,87 +1520,81 @@ msgstr "Volum, nettverk, batteri"
|
||||
msgid "<unknown>"
|
||||
msgstr "<ukjent>"
|
||||
|
||||
#: ../js/ui/status/network.js:127
|
||||
#: ../js/ui/status/network.js:125
|
||||
msgid "Wi-Fi"
|
||||
msgstr "Wi-Fi"
|
||||
|
||||
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
|
||||
#: ../js/ui/status/network.js:164
|
||||
#: ../js/ui/status/network.js:162
|
||||
msgid "disabled"
|
||||
msgstr "slått av"
|
||||
|
||||
#. Translators: this is for network devices that are physically present but are not
|
||||
#. under NetworkManager's control (and thus cannot be used in the menu)
|
||||
#: ../js/ui/status/network.js:402
|
||||
#: ../js/ui/status/network.js:397
|
||||
msgid "unmanaged"
|
||||
msgstr "ikke håndtert"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:413 ../js/ui/status/network.js:1346
|
||||
#: ../js/ui/status/network.js:408 ../js/ui/status/network.js:1301
|
||||
msgid "authentication required"
|
||||
msgstr "autentisering kreves"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
#: ../js/ui/status/network.js:423
|
||||
#: ../js/ui/status/network.js:419
|
||||
msgid "firmware missing"
|
||||
msgstr "fastvare mangler"
|
||||
|
||||
#. Translators: this is for wired network devices that are physically disconnected
|
||||
#: ../js/ui/status/network.js:430
|
||||
msgid "cable unplugged"
|
||||
msgstr "kabel koblet fra"
|
||||
|
||||
#. Translators: this is for a network device that cannot be activated (for example it
|
||||
#. is disabled by rfkill, or it has no coverage
|
||||
#: ../js/ui/status/network.js:435
|
||||
#: ../js/ui/status/network.js:423
|
||||
msgid "unavailable"
|
||||
msgstr "ikke tilgjengelig"
|
||||
|
||||
#: ../js/ui/status/network.js:437 ../js/ui/status/network.js:1348
|
||||
#: ../js/ui/status/network.js:425 ../js/ui/status/network.js:1303
|
||||
msgid "connection failed"
|
||||
msgstr "tilkobling feilet"
|
||||
|
||||
#: ../js/ui/status/network.js:490 ../js/ui/status/network.js:1236
|
||||
#: ../js/ui/status/network.js:1424
|
||||
#: ../js/ui/status/network.js:478 ../js/ui/status/network.js:1190
|
||||
msgid "More…"
|
||||
msgstr "Mer …"
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
#: ../js/ui/status/network.js:518 ../js/ui/status/network.js:1191
|
||||
#: ../js/ui/status/network.js:506 ../js/ui/status/network.js:1142
|
||||
msgid "Connected (private)"
|
||||
msgstr "Tilkoblet (privat)"
|
||||
|
||||
#: ../js/ui/status/network.js:597
|
||||
#: ../js/ui/status/network.js:572
|
||||
msgid "Wired"
|
||||
msgstr "Kablet"
|
||||
|
||||
#: ../js/ui/status/network.js:611
|
||||
#: ../js/ui/status/network.js:592
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Mobilt bredbånd"
|
||||
|
||||
#: ../js/ui/status/network.js:1522
|
||||
#: ../js/ui/status/network.js:1474
|
||||
msgid "Enable networking"
|
||||
msgstr "Slå på nettverk"
|
||||
|
||||
#: ../js/ui/status/network.js:1583
|
||||
#: ../js/ui/status/network.js:1522
|
||||
msgid "Network Settings"
|
||||
msgstr "Innstillinger for nettverk"
|
||||
|
||||
#: ../js/ui/status/network.js:1600
|
||||
#: ../js/ui/status/network.js:1539
|
||||
msgid "Network Manager"
|
||||
msgstr "Nettverkshåndtering"
|
||||
|
||||
#: ../js/ui/status/network.js:1690
|
||||
#: ../js/ui/status/network.js:1623
|
||||
msgid "Connection failed"
|
||||
msgstr "Tilkobling feilet"
|
||||
|
||||
#: ../js/ui/status/network.js:1691
|
||||
#: ../js/ui/status/network.js:1624
|
||||
msgid "Activation of network connection failed"
|
||||
msgstr "Aktivering av nettverkstilkobling feilet"
|
||||
|
||||
#: ../js/ui/status/network.js:2047
|
||||
#: ../js/ui/status/network.js:1938
|
||||
msgid "Networking is disabled"
|
||||
msgstr "Nettverk er slått av"
|
||||
|
||||
@ -1688,72 +1695,72 @@ msgctxt "device"
|
||||
msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
|
||||
#: ../js/ui/status/volume.js:124
|
||||
#: ../js/ui/status/volume.js:121
|
||||
msgid "Volume changed"
|
||||
msgstr "Volum endret"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
#: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
|
||||
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294
|
||||
msgid "Volume"
|
||||
msgstr "Volum"
|
||||
|
||||
#: ../js/ui/status/volume.js:258
|
||||
#: ../js/ui/status/volume.js:255
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofon"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:120
|
||||
#: ../js/ui/unlockDialog.js:119
|
||||
msgid "Log in as another user"
|
||||
msgstr "Logg inn som en annen bruker"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:141
|
||||
#: ../js/ui/unlockDialog.js:140
|
||||
msgid "Unlock Window"
|
||||
msgstr "Lås opp vindu"
|
||||
|
||||
#: ../js/ui/userMenu.js:193
|
||||
#: ../js/ui/userMenu.js:149
|
||||
msgid "Available"
|
||||
msgstr "Tilgjengelig"
|
||||
|
||||
#: ../js/ui/userMenu.js:196
|
||||
#: ../js/ui/userMenu.js:152
|
||||
msgid "Busy"
|
||||
msgstr "Opptatt"
|
||||
|
||||
#: ../js/ui/userMenu.js:199
|
||||
#: ../js/ui/userMenu.js:155
|
||||
msgid "Invisible"
|
||||
msgstr "Usynlig"
|
||||
|
||||
#: ../js/ui/userMenu.js:202
|
||||
#: ../js/ui/userMenu.js:158
|
||||
msgid "Away"
|
||||
msgstr "Borte"
|
||||
|
||||
#: ../js/ui/userMenu.js:205
|
||||
#: ../js/ui/userMenu.js:161
|
||||
msgid "Idle"
|
||||
msgstr "Ledig"
|
||||
|
||||
#: ../js/ui/userMenu.js:208
|
||||
#: ../js/ui/userMenu.js:164
|
||||
msgid "Offline"
|
||||
msgstr "Frakoblet"
|
||||
|
||||
#: ../js/ui/userMenu.js:781
|
||||
#: ../js/ui/userMenu.js:736
|
||||
msgid "Notifications"
|
||||
msgstr "Varslinger"
|
||||
|
||||
#: ../js/ui/userMenu.js:797
|
||||
#: ../js/ui/userMenu.js:749
|
||||
msgid "Switch User"
|
||||
msgstr "Bytt bruker"
|
||||
|
||||
#: ../js/ui/userMenu.js:802
|
||||
#: ../js/ui/userMenu.js:754
|
||||
msgid "Log Out"
|
||||
msgstr "Logg ut"
|
||||
|
||||
#: ../js/ui/userMenu.js:822
|
||||
#: ../js/ui/userMenu.js:774
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "Installer oppdateringer og start på nytt"
|
||||
|
||||
#: ../js/ui/userMenu.js:840
|
||||
#: ../js/ui/userMenu.js:792
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "Din pratestatus vil bli satt til opptatt"
|
||||
|
||||
#: ../js/ui/userMenu.js:841
|
||||
#: ../js/ui/userMenu.js:793
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
@ -1762,24 +1769,24 @@ msgstr ""
|
||||
"tilkoblingsstatus er justert for å la andre vite at du kanskje ikke ser "
|
||||
"deres meldinger."
|
||||
|
||||
#: ../js/ui/userMenu.js:888
|
||||
#: ../js/ui/userMenu.js:834
|
||||
msgid "Other users are logged in."
|
||||
msgstr "Andre brukere er logget inn."
|
||||
|
||||
#: ../js/ui/userMenu.js:893
|
||||
#: ../js/ui/userMenu.js:839
|
||||
msgid "Shutting down might cause them to lose unsaved work."
|
||||
msgstr ""
|
||||
"Hvis du slår av vil dette kunne medføre at de mister arbeid som ikke er "
|
||||
"lagret."
|
||||
|
||||
#. Translators: Remote here refers to a remote session, like a ssh login
|
||||
#: ../js/ui/userMenu.js:921
|
||||
#: ../js/ui/userMenu.js:867
|
||||
#, c-format
|
||||
msgid "%s (remote)"
|
||||
msgstr "%s (ekstern)"
|
||||
|
||||
#. Translators: Console here refers to a tty like a VT console
|
||||
#: ../js/ui/userMenu.js:924
|
||||
#: ../js/ui/userMenu.js:870
|
||||
#, c-format
|
||||
msgid "%s (console)"
|
||||
msgstr "%s (konsoll)"
|
||||
@ -1837,19 +1844,19 @@ msgstr[1] "%u innganger"
|
||||
msgid "System Sounds"
|
||||
msgstr "Systemlyder"
|
||||
|
||||
#: ../src/main.c:372
|
||||
#: ../src/main.c:353
|
||||
msgid "Print version"
|
||||
msgstr "Skriv ut versjon"
|
||||
|
||||
#: ../src/main.c:378
|
||||
#: ../src/main.c:359
|
||||
msgid "Mode used by GDM for login screen"
|
||||
msgstr "Modus som brukes av GDM for innloggingsskjermen"
|
||||
|
||||
#: ../src/main.c:384
|
||||
#: ../src/main.c:365
|
||||
msgid "Use a specific mode, e.g. \"gdm\" for login screen"
|
||||
msgstr "Bruk spesifikt modus, f.eks «gdm» for innloggingsskjerm"
|
||||
|
||||
#: ../src/main.c:390
|
||||
#: ../src/main.c:371
|
||||
msgid "List possible modes"
|
||||
msgstr "Vis mulige modi"
|
||||
|
||||
|
395
po/sk.po
395
po/sk.po
@ -10,8 +10,8 @@ msgstr ""
|
||||
"Project-Id-Version: gnome-shell\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2013-05-24 21:21+0000\n"
|
||||
"PO-Revision-Date: 2013-05-24 23:24+0100\n"
|
||||
"POT-Creation-Date: 2013-07-13 07:31+0000\n"
|
||||
"PO-Revision-Date: 2013-07-13 20:32+0200\n"
|
||||
"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
|
||||
"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
|
||||
"Language: sk\n"
|
||||
@ -384,38 +384,43 @@ msgstr "Rozšírenie"
|
||||
msgid "Select an extension to configure using the combobox above."
|
||||
msgstr "Použitím ponuky vyberte rozšírenie na nastavenie"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:371
|
||||
msgid "Session…"
|
||||
msgstr "Relácia…"
|
||||
# button
|
||||
#: ../js/gdm/loginDialog.js:308
|
||||
msgid "Choose Session"
|
||||
msgstr "Vybrať reláciu"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:326
|
||||
msgid "Session"
|
||||
msgstr "Relácia"
|
||||
|
||||
# https://bugzilla.gnome.org/show_bug.cgi?id=659972
|
||||
#. translators: this message is shown below the user list on the
|
||||
#. login screen. It can be activated to reveal an entry for
|
||||
#. manually entering the username.
|
||||
#: ../js/gdm/loginDialog.js:601
|
||||
#: ../js/gdm/loginDialog.js:533
|
||||
msgid "Not listed?"
|
||||
msgstr "Nie ste v zozname?"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:776 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
|
||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:96
|
||||
#: ../js/ui/userMenu.js:938
|
||||
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
|
||||
#: ../js/ui/userMenu.js:884
|
||||
msgid "Cancel"
|
||||
msgstr "Zrušiť"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:791
|
||||
#: ../js/gdm/loginDialog.js:833
|
||||
msgctxt "button"
|
||||
msgid "Sign In"
|
||||
msgstr "Prihlásiť sa"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:791
|
||||
#: ../js/gdm/loginDialog.js:833
|
||||
msgid "Next"
|
||||
msgstr "Ďalej"
|
||||
|
||||
#. Translators: this message is shown below the username entry field
|
||||
#. to clue the user in on how to login to the local network realm
|
||||
#: ../js/gdm/loginDialog.js:888
|
||||
#: ../js/gdm/loginDialog.js:934
|
||||
#, c-format
|
||||
msgid "(e.g., user or %s)"
|
||||
msgstr "(napr., používateľ alebo %s)"
|
||||
@ -423,12 +428,12 @@ msgstr "(napr., používateľ alebo %s)"
|
||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||
#. is not visible here since we only care about phase2 authentication
|
||||
#. (and don't even care of which one)
|
||||
#: ../js/gdm/loginDialog.js:892 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/ui/components/networkAgent.js:278
|
||||
msgid "Username: "
|
||||
msgstr "Používateľské meno: "
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1158
|
||||
#: ../js/gdm/loginDialog.js:1205
|
||||
msgid "Login Window"
|
||||
msgstr "Prihlasovacie okno"
|
||||
|
||||
@ -437,8 +442,8 @@ msgstr "Prihlasovacie okno"
|
||||
msgid "Power"
|
||||
msgstr "Napájanie"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
|
||||
#: ../js/ui/userMenu.js:816
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655
|
||||
#: ../js/ui/userMenu.js:768
|
||||
msgid "Suspend"
|
||||
msgstr "Uspať"
|
||||
|
||||
@ -446,18 +451,18 @@ msgstr "Uspať"
|
||||
msgid "Restart"
|
||||
msgstr "Reštartovať"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
|
||||
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653
|
||||
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888
|
||||
msgid "Power Off"
|
||||
msgstr "Vypnúť"
|
||||
|
||||
#: ../js/gdm/util.js:247
|
||||
#: ../js/gdm/util.js:248
|
||||
msgid "Authentication error"
|
||||
msgstr "Chyba pri overovaní totožnosti"
|
||||
|
||||
#. Translators: this message is shown below the password entry field
|
||||
#. to indicate the user can swipe their finger instead
|
||||
#: ../js/gdm/util.js:364
|
||||
#: ../js/gdm/util.js:365
|
||||
msgid "(or swipe finger)"
|
||||
msgstr "(alebo prejdite prstom)"
|
||||
|
||||
@ -476,23 +481,23 @@ msgstr "Nepodarilo sa analyzovať príkaz:"
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Spustenie „%s“ zlyhalo:"
|
||||
|
||||
#: ../js/ui/appDisplay.js:361
|
||||
#: ../js/ui/appDisplay.js:397
|
||||
msgid "Frequent"
|
||||
msgstr "Často používané"
|
||||
|
||||
#: ../js/ui/appDisplay.js:368
|
||||
#: ../js/ui/appDisplay.js:404
|
||||
msgid "All"
|
||||
msgstr "Všetky"
|
||||
|
||||
#: ../js/ui/appDisplay.js:960
|
||||
#: ../js/ui/appDisplay.js:996
|
||||
msgid "New Window"
|
||||
msgstr "Nové okno"
|
||||
|
||||
#: ../js/ui/appDisplay.js:963 ../js/ui/dash.js:284
|
||||
#: ../js/ui/appDisplay.js:999 ../js/ui/dash.js:284
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Odstrániť z obľúbených"
|
||||
|
||||
#: ../js/ui/appDisplay.js:964
|
||||
#: ../js/ui/appDisplay.js:1000
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Pridať do obľúbených"
|
||||
|
||||
@ -506,7 +511,7 @@ msgstr "Program %s bol pridaný medzi obľúbené."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "Program %s bol odstránený z obľúbených."
|
||||
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744
|
||||
msgid "Settings"
|
||||
msgstr "Nastavenia"
|
||||
|
||||
@ -631,35 +636,35 @@ msgid "S"
|
||||
msgstr "So"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:735
|
||||
#: ../js/ui/calendar.js:750
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Žiadne naplánované udalosti"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:768
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %e. %B"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:754
|
||||
#: ../js/ui/calendar.js:771
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, %e. %B %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:764
|
||||
#: ../js/ui/calendar.js:782
|
||||
msgid "Today"
|
||||
msgstr "Dnes"
|
||||
|
||||
#: ../js/ui/calendar.js:768
|
||||
#: ../js/ui/calendar.js:786
|
||||
msgid "Tomorrow"
|
||||
msgstr "Zajtra"
|
||||
|
||||
#: ../js/ui/calendar.js:779
|
||||
#: ../js/ui/calendar.js:797
|
||||
msgid "This week"
|
||||
msgstr "Tento týždeň"
|
||||
|
||||
#: ../js/ui/calendar.js:787
|
||||
#: ../js/ui/calendar.js:805
|
||||
msgid "Next week"
|
||||
msgstr "Ďalší týždeň"
|
||||
|
||||
@ -851,14 +856,14 @@ msgstr "<b>%e.</b> <b>%B</b> <b>%Y</b> o <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/components/telepathyClient.js:985
|
||||
#: ../js/ui/components/telepathyClient.js:986
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "Kontakt %s odteraz vystupuje ako %s"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1088
|
||||
#: ../js/ui/components/telepathyClient.js:1089
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "Pozvánka do %s"
|
||||
@ -866,38 +871,38 @@ msgstr "Pozvánka do %s"
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1096
|
||||
#: ../js/ui/components/telepathyClient.js:1097
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "Kontakt %s vás pozýva aby ste sa pridali do %s"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1098
|
||||
#: ../js/ui/components/telepathyClient.js:1137
|
||||
#: ../js/ui/components/telepathyClient.js:1177
|
||||
#: ../js/ui/components/telepathyClient.js:1240
|
||||
#: ../js/ui/components/telepathyClient.js:1099
|
||||
#: ../js/ui/components/telepathyClient.js:1138
|
||||
#: ../js/ui/components/telepathyClient.js:1178
|
||||
#: ../js/ui/components/telepathyClient.js:1241
|
||||
msgid "Decline"
|
||||
msgstr "Odmietnuť"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1099
|
||||
#: ../js/ui/components/telepathyClient.js:1178
|
||||
#: ../js/ui/components/telepathyClient.js:1241
|
||||
#: ../js/ui/components/telepathyClient.js:1100
|
||||
#: ../js/ui/components/telepathyClient.js:1179
|
||||
#: ../js/ui/components/telepathyClient.js:1242
|
||||
msgid "Accept"
|
||||
msgstr "Prijať"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1129
|
||||
#: ../js/ui/components/telepathyClient.js:1130
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "Videohovor od kontaktu %s"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1132
|
||||
#: ../js/ui/components/telepathyClient.js:1133
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "Hovor od kontaktu %s"
|
||||
|
||||
#. translators: this is a button label (verb), not a noun
|
||||
#: ../js/ui/components/telepathyClient.js:1139
|
||||
#: ../js/ui/components/telepathyClient.js:1140
|
||||
msgid "Answer"
|
||||
msgstr "Prijať hovor"
|
||||
|
||||
@ -906,110 +911,110 @@ msgstr "Prijať hovor"
|
||||
#. * file name. The string will be something
|
||||
#. * like: "Alice is sending you test.ogg"
|
||||
#.
|
||||
#: ../js/ui/components/telepathyClient.js:1171
|
||||
#: ../js/ui/components/telepathyClient.js:1172
|
||||
#, c-format
|
||||
msgid "%s is sending you %s"
|
||||
msgstr "Kontakt %s vám posiela %s"
|
||||
|
||||
#. To translators: The parameter is the contact's alias
|
||||
#: ../js/ui/components/telepathyClient.js:1206
|
||||
#: ../js/ui/components/telepathyClient.js:1207
|
||||
#, c-format
|
||||
msgid "%s would like permission to see when you are online"
|
||||
msgstr "Kontakt %s by chcel získať oprávnenie vidieť, kedy ste pripojený"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1298
|
||||
#: ../js/ui/components/telepathyClient.js:1299
|
||||
msgid "Network error"
|
||||
msgstr "Chyba siete"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1300
|
||||
#: ../js/ui/components/telepathyClient.js:1301
|
||||
msgid "Authentication failed"
|
||||
msgstr "Overenie totožnosti zlyhalo"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1302
|
||||
#: ../js/ui/components/telepathyClient.js:1303
|
||||
msgid "Encryption error"
|
||||
msgstr "Chyba šifrovania"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1304
|
||||
#: ../js/ui/components/telepathyClient.js:1305
|
||||
msgid "Certificate not provided"
|
||||
msgstr "Neposkytnutý certifikát"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1306
|
||||
#: ../js/ui/components/telepathyClient.js:1307
|
||||
msgid "Certificate untrusted"
|
||||
msgstr "Nedôveryhodný certifikát"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1308
|
||||
#: ../js/ui/components/telepathyClient.js:1309
|
||||
msgid "Certificate expired"
|
||||
msgstr "Certifikát s ukončenou platnosťou"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1310
|
||||
#: ../js/ui/components/telepathyClient.js:1311
|
||||
msgid "Certificate not activated"
|
||||
msgstr "Neaktivovaný certifikát"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1312
|
||||
#: ../js/ui/components/telepathyClient.js:1313
|
||||
msgid "Certificate hostname mismatch"
|
||||
msgstr "Certifikát s nesúhlasným názvom hostiteľa"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1314
|
||||
#: ../js/ui/components/telepathyClient.js:1315
|
||||
msgid "Certificate fingerprint mismatch"
|
||||
msgstr "Certifikát s nesúhlasným odtlačkom"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1316
|
||||
#: ../js/ui/components/telepathyClient.js:1317
|
||||
msgid "Certificate self-signed"
|
||||
msgstr "Sebou podpísaný certifikát"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1318
|
||||
#: ../js/ui/components/telepathyClient.js:1319
|
||||
msgid "Status is set to offline"
|
||||
msgstr "Stav je nastavený na odhlásený"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1320
|
||||
#: ../js/ui/components/telepathyClient.js:1321
|
||||
msgid "Encryption is not available"
|
||||
msgstr "Šifrovanie nie je dostupné"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1322
|
||||
#: ../js/ui/components/telepathyClient.js:1323
|
||||
msgid "Certificate is invalid"
|
||||
msgstr "Certifikát je neplatný"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1324
|
||||
#: ../js/ui/components/telepathyClient.js:1325
|
||||
msgid "Connection has been refused"
|
||||
msgstr "Pripojenie bolo odmietnuté"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1326
|
||||
#: ../js/ui/components/telepathyClient.js:1327
|
||||
msgid "Connection can't be established"
|
||||
msgstr "Nedá sa nadviazať spojenie"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1328
|
||||
#: ../js/ui/components/telepathyClient.js:1329
|
||||
msgid "Connection has been lost"
|
||||
msgstr "Spojenie sa stratilo"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1330
|
||||
#: ../js/ui/components/telepathyClient.js:1331
|
||||
msgid "This account is already connected to the server"
|
||||
msgstr "Tento účet je už pripojený k serveru"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1332
|
||||
#: ../js/ui/components/telepathyClient.js:1333
|
||||
msgid ""
|
||||
"Connection has been replaced by a new connection using the same resource"
|
||||
msgstr "Pripojenie bolo nahradené novým, ktoré používa rovnaký zdroj"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1334
|
||||
#: ../js/ui/components/telepathyClient.js:1335
|
||||
msgid "The account already exists on the server"
|
||||
msgstr "Účet na serveri už existuje"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1336
|
||||
#: ../js/ui/components/telepathyClient.js:1337
|
||||
msgid "Server is currently too busy to handle the connection"
|
||||
msgstr "Server je momentálne príliš zaneprázdnený na zvládnutie pripojenia"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1338
|
||||
#: ../js/ui/components/telepathyClient.js:1339
|
||||
msgid "Certificate has been revoked"
|
||||
msgstr "Certifikát bol zrušený"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1340
|
||||
#: ../js/ui/components/telepathyClient.js:1341
|
||||
msgid ""
|
||||
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
|
||||
msgstr ""
|
||||
"Šifrovací algoritmus používaný certifikátom nie je bezpečný alebo je "
|
||||
"kryptograficky slabý"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1342
|
||||
#: ../js/ui/components/telepathyClient.js:1343
|
||||
msgid ""
|
||||
"The length of the server certificate, or the depth of the server certificate "
|
||||
"chain, exceed the limits imposed by the cryptography library"
|
||||
@ -1017,22 +1022,22 @@ msgstr ""
|
||||
"Dĺžka certifikátu servera, alebo hĺbka reťazca certifikátu servera presahuje "
|
||||
"limit stanovený kryptografickou knižnicou."
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1344
|
||||
#: ../js/ui/components/telepathyClient.js:1345
|
||||
msgid "Internal error"
|
||||
msgstr "Vnútorná chyba"
|
||||
|
||||
#. translators: argument is the account name, like
|
||||
#. * name@jabber.org for example.
|
||||
#: ../js/ui/components/telepathyClient.js:1354
|
||||
#: ../js/ui/components/telepathyClient.js:1355
|
||||
#, c-format
|
||||
msgid "Unable to connect to %s"
|
||||
msgstr "Nepodarilo sa pripojiť účet %s"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1359
|
||||
#: ../js/ui/components/telepathyClient.js:1360
|
||||
msgid "View account"
|
||||
msgstr "Zobraziť účet"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1398
|
||||
#: ../js/ui/components/telepathyClient.js:1399
|
||||
msgid "Unknown reason"
|
||||
msgstr "Neznámy dôvod"
|
||||
|
||||
@ -1047,26 +1052,26 @@ msgstr "Zobrazí aplikácie"
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/dash.js:435
|
||||
#: ../js/ui/dash.js:439
|
||||
msgid "Dash"
|
||||
msgstr "Dok"
|
||||
|
||||
#: ../js/ui/dateMenu.js:86
|
||||
#: ../js/ui/dateMenu.js:85
|
||||
msgid "Open Calendar"
|
||||
msgstr "Otvoriť kalendár"
|
||||
|
||||
#: ../js/ui/dateMenu.js:90
|
||||
#: ../js/ui/dateMenu.js:89
|
||||
msgid "Open Clocks"
|
||||
msgstr "Otvoriť hodiny"
|
||||
|
||||
#: ../js/ui/dateMenu.js:97
|
||||
#: ../js/ui/dateMenu.js:96
|
||||
msgid "Date & Time Settings"
|
||||
msgstr "Nastavenia dátumu a času"
|
||||
|
||||
#. Translators: This is the date format to use when the calendar popup is
|
||||
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
#.
|
||||
#: ../js/ui/dateMenu.js:208
|
||||
#: ../js/ui/dateMenu.js:201
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%A, %e. %B %Y"
|
||||
|
||||
@ -1175,7 +1180,7 @@ msgstr "Inštalovať"
|
||||
msgid "Download and install '%s' from extensions.gnome.org?"
|
||||
msgstr "Stiahnuť a nainštalovať „%s“ z extensions.gnome.org?"
|
||||
|
||||
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
|
||||
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:333
|
||||
#: ../js/ui/status/power.js:211
|
||||
msgid "Keyboard"
|
||||
msgstr "Klávesnica"
|
||||
@ -1229,33 +1234,33 @@ msgstr "Zobraziť zdroj"
|
||||
msgid "Web Page"
|
||||
msgstr "Webová stránka"
|
||||
|
||||
#: ../js/ui/messageTray.js:1182
|
||||
#: ../js/ui/messageTray.js:1241
|
||||
msgid "Open"
|
||||
msgstr "Otvoriť"
|
||||
|
||||
#: ../js/ui/messageTray.js:1189
|
||||
#: ../js/ui/messageTray.js:1248
|
||||
msgid "Remove"
|
||||
msgstr "Odstrániť"
|
||||
|
||||
#: ../js/ui/messageTray.js:1501
|
||||
#: ../js/ui/messageTray.js:1560
|
||||
msgid "Clear Messages"
|
||||
msgstr "Vymazať správy"
|
||||
|
||||
#: ../js/ui/messageTray.js:1528
|
||||
#: ../js/ui/messageTray.js:1587
|
||||
msgid "Notification Settings"
|
||||
msgstr "Nastavenia oznámení"
|
||||
|
||||
#: ../js/ui/messageTray.js:1707
|
||||
#: ../js/ui/messageTray.js:1770
|
||||
msgid "No Messages"
|
||||
msgstr "Žiadne správy"
|
||||
|
||||
# DK: zvazoval som pouzit "Panel správ"
|
||||
# neviem co bude vhodnejsie ako preklad "tray"
|
||||
#: ../js/ui/messageTray.js:1780
|
||||
#: ../js/ui/messageTray.js:1842
|
||||
msgid "Message Tray"
|
||||
msgstr "Lišta správ"
|
||||
|
||||
#: ../js/ui/messageTray.js:2800
|
||||
#: ../js/ui/messageTray.js:2854
|
||||
msgid "System Information"
|
||||
msgstr "Informácie o systéme"
|
||||
|
||||
@ -1264,7 +1269,7 @@ msgctxt "program"
|
||||
msgid "Unknown"
|
||||
msgstr "Neznámy"
|
||||
|
||||
#: ../js/ui/overviewControls.js:472 ../js/ui/screenShield.js:150
|
||||
#: ../js/ui/overviewControls.js:474 ../js/ui/screenShield.js:150
|
||||
#, c-format
|
||||
msgid "%d new message"
|
||||
msgid_plural "%d new messages"
|
||||
@ -1289,17 +1294,17 @@ msgstr "Prehľad"
|
||||
msgid "Type to search…"
|
||||
msgstr "Zadajte text na vyhľadanie…"
|
||||
|
||||
#: ../js/ui/panel.js:642
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Quit"
|
||||
msgstr "Ukončiť"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:693
|
||||
#: ../js/ui/panel.js:618
|
||||
msgid "Activities"
|
||||
msgstr "Aktivity"
|
||||
|
||||
#: ../js/ui/panel.js:989
|
||||
#: ../js/ui/panel.js:914
|
||||
msgid "Top Bar"
|
||||
msgstr "Horná lišta"
|
||||
|
||||
@ -1308,7 +1313,7 @@ msgstr "Horná lišta"
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:738
|
||||
#: ../js/ui/popupMenu.js:549
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
@ -1337,7 +1342,7 @@ msgstr[0] "%d nových oznámení"
|
||||
msgstr[1] "%d nové oznámenie"
|
||||
msgstr[2] "%d nové oznámenia"
|
||||
|
||||
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:807
|
||||
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759
|
||||
msgid "Lock"
|
||||
msgstr "Uzamknúť"
|
||||
|
||||
@ -1352,19 +1357,19 @@ msgstr "Prostredie GNOME vyžaduje uzamknutie obrazovky"
|
||||
#.
|
||||
#. XXX: another option is to kick the user into the gdm login
|
||||
#. screen, where we're not affected by grabs
|
||||
#: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
|
||||
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1215
|
||||
msgid "Unable to lock"
|
||||
msgstr "Nepodarilo sa uzamknúť obrazovku"
|
||||
|
||||
#: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
|
||||
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1216
|
||||
msgid "Lock was blocked by an application"
|
||||
msgstr "Uzamknutie bolo zablokované aplikáciou"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:453
|
||||
#: ../js/ui/searchDisplay.js:445
|
||||
msgid "Searching…"
|
||||
msgstr "Hľadá sa…"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:497
|
||||
#: ../js/ui/searchDisplay.js:489
|
||||
msgid "No results."
|
||||
msgstr "Žiadne výsledky."
|
||||
|
||||
@ -1392,7 +1397,7 @@ msgstr "Heslo"
|
||||
msgid "Remember Password"
|
||||
msgstr "Zapamätať heslo"
|
||||
|
||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:109
|
||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108
|
||||
msgid "Unlock"
|
||||
msgstr "Odblokovať"
|
||||
|
||||
@ -1448,9 +1453,9 @@ msgid "Large Text"
|
||||
msgstr "Veľký text"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
|
||||
#: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
|
||||
#: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
|
||||
#: ../js/ui/status/network.js:739
|
||||
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327
|
||||
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391
|
||||
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713
|
||||
msgid "Bluetooth"
|
||||
msgstr "Bluetooth"
|
||||
|
||||
@ -1471,106 +1476,117 @@ msgid "Bluetooth Settings"
|
||||
msgstr "Nastavenia Bluetooth"
|
||||
|
||||
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
|
||||
#: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:142
|
||||
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140
|
||||
msgid "hardware disabled"
|
||||
msgstr "hardvér zakázaný"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:197
|
||||
#: ../js/ui/status/bluetooth.js:198
|
||||
msgid "Connection"
|
||||
msgstr "Pripojenie"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:404
|
||||
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399
|
||||
msgid "disconnecting..."
|
||||
msgstr "odpája sa…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:410
|
||||
#: ../js/ui/status/network.js:1343
|
||||
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405
|
||||
#: ../js/ui/status/network.js:1298
|
||||
msgid "connecting..."
|
||||
msgstr "pripája sa…"
|
||||
|
||||
# menu item
|
||||
#: ../js/ui/status/bluetooth.js:239
|
||||
#: ../js/ui/status/bluetooth.js:240
|
||||
msgid "Send Files…"
|
||||
msgstr "Odoslať súbory…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:246
|
||||
#: ../js/ui/status/bluetooth.js:247
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Nastavenia klávesnice"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:249
|
||||
#: ../js/ui/status/bluetooth.js:250
|
||||
msgid "Mouse Settings"
|
||||
msgstr "Nastavenia myši"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
|
||||
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313
|
||||
msgid "Sound Settings"
|
||||
msgstr "Nastavenia zvuku"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:322
|
||||
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356
|
||||
#, c-format
|
||||
msgid "Authorization request from %s"
|
||||
msgstr "Žiadosť o potvrdenie prístupu od %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:328
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "Zariadenie %s chce pristupovať k službe „%s“"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:330
|
||||
msgid "Always grant access"
|
||||
msgstr "Vždy povoliť prístup"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:331
|
||||
msgid "Grant this time only"
|
||||
msgstr "Povoliť iba teraz"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:332
|
||||
msgid "Reject"
|
||||
msgstr "Odmietnuť"
|
||||
|
||||
#. Translators: argument is the device short name
|
||||
#: ../js/ui/status/bluetooth.js:359
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Potvrdenie spárovania pre %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
|
||||
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399
|
||||
#: ../js/ui/status/bluetooth.js:430
|
||||
#, c-format
|
||||
msgid "Device %s wants to pair with this computer"
|
||||
msgstr "Zariadenie %s sa chce spárovať s týmto počítačom"
|
||||
|
||||
# button
|
||||
#: ../js/ui/status/bluetooth.js:336
|
||||
msgid "Allow"
|
||||
msgstr "Povoliť"
|
||||
|
||||
# button
|
||||
#: ../js/ui/status/bluetooth.js:337
|
||||
msgid "Deny"
|
||||
msgstr "Zakázať"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:362
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "Zariadenie %s chce pristupovať k službe „%s“"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:364
|
||||
msgid "Always grant access"
|
||||
msgstr "Vždy povoliť prístup"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:365
|
||||
msgid "Grant this time only"
|
||||
msgstr "Povoliť iba teraz"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:366
|
||||
msgid "Reject"
|
||||
msgstr "Odmietnuť"
|
||||
|
||||
#. Translators: argument is the device short name
|
||||
#: ../js/ui/status/bluetooth.js:393
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Potvrdenie spárovania pre %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:400
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Please confirm whether the Passkey '%06d' matches the one on the device."
|
||||
msgstr "Prosím, potvrďte, či sa heslo „%06d“ zhoduje s tým na zariadení."
|
||||
|
||||
#. Translators: this is the verb, not the noun
|
||||
#: ../js/ui/status/bluetooth.js:369
|
||||
#: ../js/ui/status/bluetooth.js:403
|
||||
msgid "Matches"
|
||||
msgstr "Zhoduje sa"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:370
|
||||
#: ../js/ui/status/bluetooth.js:404
|
||||
msgid "Does not match"
|
||||
msgstr "Nezhoduje sa"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:389
|
||||
#: ../js/ui/status/bluetooth.js:423
|
||||
#, c-format
|
||||
msgid "Pairing request for %s"
|
||||
msgstr "Požiadavka na spárovanie pre %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:397
|
||||
#: ../js/ui/status/bluetooth.js:431
|
||||
msgid "Please enter the PIN mentioned on the device."
|
||||
msgstr "Zadajte PIN, ktoré je uvedené na zariadení."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:414
|
||||
#: ../js/ui/status/bluetooth.js:448
|
||||
msgid "OK"
|
||||
msgstr "Ok"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:368
|
||||
#: ../js/ui/status/keyboard.js:396
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "Zobraziť rozloženie klávesnice"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:373
|
||||
#: ../js/ui/status/keyboard.js:401
|
||||
msgid "Region & Language Settings"
|
||||
msgstr "Miestne a jazykové nastavenia"
|
||||
|
||||
@ -1583,87 +1599,81 @@ msgstr "Hlasitosť, sieť, batéria"
|
||||
msgid "<unknown>"
|
||||
msgstr "<neznáme>"
|
||||
|
||||
#: ../js/ui/status/network.js:127
|
||||
#: ../js/ui/status/network.js:125
|
||||
msgid "Wi-Fi"
|
||||
msgstr "Wi-Fi"
|
||||
|
||||
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
|
||||
#: ../js/ui/status/network.js:164
|
||||
#: ../js/ui/status/network.js:162
|
||||
msgid "disabled"
|
||||
msgstr "zakázané"
|
||||
|
||||
#. Translators: this is for network devices that are physically present but are not
|
||||
#. under NetworkManager's control (and thus cannot be used in the menu)
|
||||
#: ../js/ui/status/network.js:402
|
||||
#: ../js/ui/status/network.js:397
|
||||
msgid "unmanaged"
|
||||
msgstr "nespravované"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:413 ../js/ui/status/network.js:1346
|
||||
#: ../js/ui/status/network.js:408 ../js/ui/status/network.js:1301
|
||||
msgid "authentication required"
|
||||
msgstr "požaduje sa overenie totožnosti"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
#: ../js/ui/status/network.js:423
|
||||
#: ../js/ui/status/network.js:419
|
||||
msgid "firmware missing"
|
||||
msgstr "chýba firmvér"
|
||||
|
||||
#. Translators: this is for wired network devices that are physically disconnected
|
||||
#: ../js/ui/status/network.js:430
|
||||
msgid "cable unplugged"
|
||||
msgstr "kábel odpojený"
|
||||
|
||||
#. Translators: this is for a network device that cannot be activated (for example it
|
||||
#. is disabled by rfkill, or it has no coverage
|
||||
#: ../js/ui/status/network.js:435
|
||||
#: ../js/ui/status/network.js:423
|
||||
msgid "unavailable"
|
||||
msgstr "nedostupné"
|
||||
|
||||
#: ../js/ui/status/network.js:437 ../js/ui/status/network.js:1348
|
||||
#: ../js/ui/status/network.js:425 ../js/ui/status/network.js:1303
|
||||
msgid "connection failed"
|
||||
msgstr "pripojenie zlyhalo"
|
||||
|
||||
#: ../js/ui/status/network.js:490 ../js/ui/status/network.js:1236
|
||||
#: ../js/ui/status/network.js:1424
|
||||
#: ../js/ui/status/network.js:478 ../js/ui/status/network.js:1190
|
||||
msgid "More…"
|
||||
msgstr "Viac…"
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
#: ../js/ui/status/network.js:518 ../js/ui/status/network.js:1191
|
||||
#: ../js/ui/status/network.js:506 ../js/ui/status/network.js:1142
|
||||
msgid "Connected (private)"
|
||||
msgstr "Pripojené (súkromne)"
|
||||
|
||||
#: ../js/ui/status/network.js:597
|
||||
#: ../js/ui/status/network.js:572
|
||||
msgid "Wired"
|
||||
msgstr "Drôtové pripojenie"
|
||||
|
||||
#: ../js/ui/status/network.js:611
|
||||
#: ../js/ui/status/network.js:592
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Širokopásmové pripojenie"
|
||||
|
||||
#: ../js/ui/status/network.js:1522
|
||||
#: ../js/ui/status/network.js:1474
|
||||
msgid "Enable networking"
|
||||
msgstr "Povoliť sieť"
|
||||
|
||||
#: ../js/ui/status/network.js:1583
|
||||
#: ../js/ui/status/network.js:1522
|
||||
msgid "Network Settings"
|
||||
msgstr "Nastavenia siete"
|
||||
|
||||
#: ../js/ui/status/network.js:1600
|
||||
#: ../js/ui/status/network.js:1539
|
||||
msgid "Network Manager"
|
||||
msgstr "Správca siete"
|
||||
|
||||
#: ../js/ui/status/network.js:1690
|
||||
#: ../js/ui/status/network.js:1623
|
||||
msgid "Connection failed"
|
||||
msgstr "Pripojenie zlyhalo"
|
||||
|
||||
#: ../js/ui/status/network.js:1691
|
||||
#: ../js/ui/status/network.js:1624
|
||||
msgid "Activation of network connection failed"
|
||||
msgstr "Aktivácia pripojenia k sieti zlyhala"
|
||||
|
||||
#: ../js/ui/status/network.js:2047
|
||||
#: ../js/ui/status/network.js:1938
|
||||
msgid "Networking is disabled"
|
||||
msgstr "Sieť je zakázaná"
|
||||
|
||||
@ -1768,72 +1778,72 @@ msgctxt "device"
|
||||
msgid "Unknown"
|
||||
msgstr "Neznáme"
|
||||
|
||||
#: ../js/ui/status/volume.js:124
|
||||
#: ../js/ui/status/volume.js:121
|
||||
msgid "Volume changed"
|
||||
msgstr "Hlasitosť bola zmenená"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
#: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
|
||||
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294
|
||||
msgid "Volume"
|
||||
msgstr "Hlasitosť"
|
||||
|
||||
#: ../js/ui/status/volume.js:258
|
||||
#: ../js/ui/status/volume.js:255
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofón"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:120
|
||||
#: ../js/ui/unlockDialog.js:119
|
||||
msgid "Log in as another user"
|
||||
msgstr "Prihlásiť ako iný používateľ"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:141
|
||||
#: ../js/ui/unlockDialog.js:140
|
||||
msgid "Unlock Window"
|
||||
msgstr "Odomykacie okno"
|
||||
|
||||
#: ../js/ui/userMenu.js:193
|
||||
#: ../js/ui/userMenu.js:149
|
||||
msgid "Available"
|
||||
msgstr "Prítomný"
|
||||
|
||||
#: ../js/ui/userMenu.js:196
|
||||
#: ../js/ui/userMenu.js:152
|
||||
msgid "Busy"
|
||||
msgstr "Zaneprázdnený"
|
||||
|
||||
#: ../js/ui/userMenu.js:199
|
||||
#: ../js/ui/userMenu.js:155
|
||||
msgid "Invisible"
|
||||
msgstr "Neviditeľný"
|
||||
|
||||
#: ../js/ui/userMenu.js:202
|
||||
#: ../js/ui/userMenu.js:158
|
||||
msgid "Away"
|
||||
msgstr "Neprítomný"
|
||||
|
||||
#: ../js/ui/userMenu.js:205
|
||||
#: ../js/ui/userMenu.js:161
|
||||
msgid "Idle"
|
||||
msgstr "Nečinný"
|
||||
|
||||
#: ../js/ui/userMenu.js:208
|
||||
#: ../js/ui/userMenu.js:164
|
||||
msgid "Offline"
|
||||
msgstr "Odhlásený"
|
||||
|
||||
#: ../js/ui/userMenu.js:781
|
||||
#: ../js/ui/userMenu.js:736
|
||||
msgid "Notifications"
|
||||
msgstr "Upozornenia"
|
||||
|
||||
#: ../js/ui/userMenu.js:797
|
||||
#: ../js/ui/userMenu.js:749
|
||||
msgid "Switch User"
|
||||
msgstr "Prepnúť používateľa"
|
||||
|
||||
#: ../js/ui/userMenu.js:802
|
||||
#: ../js/ui/userMenu.js:754
|
||||
msgid "Log Out"
|
||||
msgstr "Odhlásiť sa"
|
||||
|
||||
#: ../js/ui/userMenu.js:822
|
||||
#: ../js/ui/userMenu.js:774
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "Nainštalovať aktualizácie a reštartovať"
|
||||
|
||||
#: ../js/ui/userMenu.js:840
|
||||
#: ../js/ui/userMenu.js:792
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "Váš stav bude nastavený na zaneprázdnený"
|
||||
|
||||
#: ../js/ui/userMenu.js:841
|
||||
#: ../js/ui/userMenu.js:793
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
@ -1841,22 +1851,22 @@ msgstr ""
|
||||
"Oznámenia, vrátane správ rozhovoru, sú teraz zakázané. Váš stav online bol "
|
||||
"nastavený tak, aby ostatní vedeli, že nemusíte vidieť ich správy."
|
||||
|
||||
#: ../js/ui/userMenu.js:888
|
||||
#: ../js/ui/userMenu.js:834
|
||||
msgid "Other users are logged in."
|
||||
msgstr "Sú prihlásení iní používatelia."
|
||||
|
||||
#: ../js/ui/userMenu.js:893
|
||||
#: ../js/ui/userMenu.js:839
|
||||
msgid "Shutting down might cause them to lose unsaved work."
|
||||
msgstr "Vypnutie môže spôsobiť stratu neuloženej práce."
|
||||
|
||||
#. Translators: Remote here refers to a remote session, like a ssh login
|
||||
#: ../js/ui/userMenu.js:921
|
||||
#: ../js/ui/userMenu.js:867
|
||||
#, c-format
|
||||
msgid "%s (remote)"
|
||||
msgstr "%s (vzdialená relácia)"
|
||||
|
||||
#. Translators: Console here refers to a tty like a VT console
|
||||
#: ../js/ui/userMenu.js:924
|
||||
#: ../js/ui/userMenu.js:870
|
||||
#, c-format
|
||||
msgid "%s (console)"
|
||||
msgstr "%s (konzola)"
|
||||
@ -1918,19 +1928,19 @@ msgstr[2] "%u vstupy"
|
||||
msgid "System Sounds"
|
||||
msgstr "Systémové zvuky"
|
||||
|
||||
#: ../src/main.c:372
|
||||
#: ../src/main.c:353
|
||||
msgid "Print version"
|
||||
msgstr "Verzia pre tlač"
|
||||
|
||||
#: ../src/main.c:378
|
||||
#: ../src/main.c:359
|
||||
msgid "Mode used by GDM for login screen"
|
||||
msgstr "Režim používaný GDM pre prihlasovaciu obrazovku"
|
||||
|
||||
#: ../src/main.c:384
|
||||
#: ../src/main.c:365
|
||||
msgid "Use a specific mode, e.g. \"gdm\" for login screen"
|
||||
msgstr "Použitie zvláštneho režimu, napr. „gdm“ pre prihlasovaciu obrazovku"
|
||||
|
||||
#: ../src/main.c:390
|
||||
#: ../src/main.c:371
|
||||
msgid "List possible modes"
|
||||
msgstr "Zoznam možných režimov"
|
||||
|
||||
@ -1952,6 +1962,9 @@ msgstr "Heslo nemôže byť prázdne"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "Dialógové okno overenia totožnosti bolo zatvorené používateľom"
|
||||
|
||||
#~ msgid "cable unplugged"
|
||||
#~ msgstr "kábel odpojený"
|
||||
|
||||
#~ msgid "Whether to collect stats about applications usage"
|
||||
#~ msgstr "Či sa majú zhromažďovať štatistické údaje o používaní aplikácií"
|
||||
|
||||
|
562
po/zh_CN.po
562
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
@ -214,6 +214,7 @@ shell_app_create_icon_texture (ShellApp *app,
|
||||
typedef struct {
|
||||
ShellApp *app;
|
||||
int size;
|
||||
ClutterTextDirection direction;
|
||||
} CreateFadedIconData;
|
||||
|
||||
static CoglHandle
|
||||
@ -231,7 +232,7 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
|
||||
guint8 n_channels;
|
||||
gboolean have_alpha;
|
||||
gint fade_start;
|
||||
gint fade_range;
|
||||
gint fade_end;
|
||||
guint i, j;
|
||||
guint pixbuf_byte_size;
|
||||
guint8 *orig_pixels;
|
||||
@ -283,14 +284,26 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
|
||||
pixels = g_malloc0 (rowstride * height);
|
||||
memcpy (pixels, orig_pixels, pixbuf_byte_size);
|
||||
|
||||
fade_start = width / 2;
|
||||
fade_range = width - fade_start;
|
||||
for (i = fade_start; i < width; i++)
|
||||
/* fade on the right side for LTR, left side for RTL */
|
||||
if (data->direction == CLUTTER_TEXT_DIRECTION_LTR)
|
||||
{
|
||||
fade_start = width / 2;
|
||||
fade_end = width;
|
||||
}
|
||||
else
|
||||
{
|
||||
fade_start = 0;
|
||||
fade_end = width / 2;
|
||||
}
|
||||
|
||||
for (i = fade_start; i < fade_end; i++)
|
||||
{
|
||||
for (j = 0; j < height; j++)
|
||||
{
|
||||
guchar *pixel = &pixels[j * rowstride + i * n_channels];
|
||||
float fade = 1.0 - ((float) i - fade_start) / fade_range;
|
||||
float fade = ((float) i - fade_start) / (fade_end - fade_start);
|
||||
if (data->direction == CLUTTER_TEXT_DIRECTION_LTR)
|
||||
fade = 1.0 - fade;
|
||||
pixel[0] = 0.5 + pixel[0] * fade;
|
||||
pixel[1] = 0.5 + pixel[1] * fade;
|
||||
pixel[2] = 0.5 + pixel[2] * fade;
|
||||
@ -316,13 +329,14 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
|
||||
* shell_app_get_faded_icon:
|
||||
* @app: A #ShellApp
|
||||
* @size: Size in pixels
|
||||
* @direction: Whether to fade on the left or right
|
||||
*
|
||||
* Return an actor with a horizontally faded look.
|
||||
*
|
||||
* Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_app_get_faded_icon (ShellApp *app, int size)
|
||||
shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection direction)
|
||||
{
|
||||
CoglHandle texture;
|
||||
ClutterActor *result;
|
||||
@ -338,9 +352,13 @@ shell_app_get_faded_icon (ShellApp *app, int size)
|
||||
|
||||
/* Use icon: prefix so that we get evicted from the cache on
|
||||
* icon theme changes. */
|
||||
cache_key = g_strdup_printf ("icon:%s,size=%d,faded", shell_app_get_id (app), size);
|
||||
cache_key = g_strdup_printf ("icon:%s,size=%d,faded-%s",
|
||||
shell_app_get_id (app),
|
||||
size,
|
||||
direction == CLUTTER_TEXT_DIRECTION_RTL ? "rtl" : "ltr");
|
||||
data.app = app;
|
||||
data.size = size;
|
||||
data.direction = direction;
|
||||
texture = st_texture_cache_load (st_texture_cache_get_default (),
|
||||
cache_key,
|
||||
ST_TEXTURE_CACHE_POLICY_FOREVER,
|
||||
|
@ -43,7 +43,7 @@ GMenuTreeEntry *shell_app_get_tree_entry (ShellApp *app);
|
||||
GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
|
||||
|
||||
ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size);
|
||||
ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size);
|
||||
ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection direction);
|
||||
const char *shell_app_get_name (ShellApp *app);
|
||||
const char *shell_app_get_description (ShellApp *app);
|
||||
gboolean shell_app_is_window_backed (ShellApp *app);
|
||||
|
@ -70,7 +70,6 @@ struct _ShellGlobal {
|
||||
GtkWindow *grab_notifier;
|
||||
gboolean gtk_grab_active;
|
||||
|
||||
ShellStageInputMode input_mode;
|
||||
XserverRegion input_region;
|
||||
|
||||
GjsContext *js_context;
|
||||
@ -94,6 +93,8 @@ struct _ShellGlobal {
|
||||
guint32 xdnd_timestamp;
|
||||
|
||||
gint64 last_gc_end_time;
|
||||
|
||||
gboolean has_modal;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -106,7 +107,6 @@ enum {
|
||||
PROP_SCREEN_WIDTH,
|
||||
PROP_SCREEN_HEIGHT,
|
||||
PROP_STAGE,
|
||||
PROP_STAGE_INPUT_MODE,
|
||||
PROP_WINDOW_GROUP,
|
||||
PROP_TOP_WINDOW_GROUP,
|
||||
PROP_WINDOW_MANAGER,
|
||||
@ -141,9 +141,6 @@ shell_global_set_property(GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_STAGE_INPUT_MODE:
|
||||
shell_global_set_stage_input_mode (global, g_value_get_enum (value));
|
||||
break;
|
||||
case PROP_SESSION_MODE:
|
||||
g_clear_pointer (&global->session_mode, g_free);
|
||||
global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
|
||||
@ -195,9 +192,6 @@ shell_global_get_property(GObject *object,
|
||||
case PROP_STAGE:
|
||||
g_value_set_object (value, global->stage);
|
||||
break;
|
||||
case PROP_STAGE_INPUT_MODE:
|
||||
g_value_set_enum (value, global->input_mode);
|
||||
break;
|
||||
case PROP_WINDOW_GROUP:
|
||||
g_value_set_object (value, meta_get_window_group_for_screen (global->meta_screen));
|
||||
break;
|
||||
@ -278,8 +272,6 @@ shell_global_init (ShellGlobal *global)
|
||||
g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global);
|
||||
global->gtk_grab_active = FALSE;
|
||||
|
||||
global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
|
||||
|
||||
global->sound_context = ca_gtk_context_get ();
|
||||
ca_context_change_props (global->sound_context,
|
||||
CA_PROP_APPLICATION_NAME, "GNOME Shell",
|
||||
@ -417,14 +409,6 @@ shell_global_class_init (ShellGlobalClass *klass)
|
||||
"Stage holding the desktop scene graph",
|
||||
CLUTTER_TYPE_ACTOR,
|
||||
G_PARAM_READABLE));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STAGE_INPUT_MODE,
|
||||
g_param_spec_enum ("stage-input-mode",
|
||||
"Stage input mode",
|
||||
"The stage input mode",
|
||||
SHELL_TYPE_STAGE_INPUT_MODE,
|
||||
SHELL_STAGE_INPUT_MODE_NORMAL,
|
||||
G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_WINDOW_GROUP,
|
||||
g_param_spec_object ("window-group",
|
||||
@ -529,6 +513,18 @@ shell_global_get (void)
|
||||
return the_object;
|
||||
}
|
||||
|
||||
static guint32
|
||||
get_current_time_maybe_roundtrip (ShellGlobal *global)
|
||||
{
|
||||
guint32 time;
|
||||
|
||||
time = shell_global_get_current_time (global);
|
||||
if (time != CurrentTime)
|
||||
return time;
|
||||
|
||||
return meta_display_get_current_time_roundtrip (global->meta_display);
|
||||
}
|
||||
|
||||
static void
|
||||
focus_window_changed (MetaDisplay *display,
|
||||
GParamSpec *param,
|
||||
@ -536,58 +532,71 @@ focus_window_changed (MetaDisplay *display,
|
||||
{
|
||||
ShellGlobal *global = user_data;
|
||||
|
||||
if (global->input_mode == SHELL_STAGE_INPUT_MODE_FOCUSED &&
|
||||
meta_display_get_focus_window (display) != NULL)
|
||||
shell_global_set_stage_input_mode (global, SHELL_STAGE_INPUT_MODE_NORMAL);
|
||||
if (global->has_modal)
|
||||
return;
|
||||
|
||||
/* If the stage window became unfocused, drop the key focus
|
||||
* on Clutter's side. */
|
||||
if (!meta_stage_is_focused (global->meta_screen))
|
||||
clutter_stage_set_key_focus (global->stage, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_set_stage_input_mode:
|
||||
* @global: the #ShellGlobal
|
||||
* @mode: the stage input mode
|
||||
*
|
||||
* Sets the input mode of the stage; when @mode is
|
||||
* %SHELL_STAGE_INPUT_MODE_NORMAL, then the stage accepts clicks in
|
||||
* the region defined by shell_global_set_stage_input_region() but
|
||||
* passes through clicks outside that region. When it is
|
||||
* %SHELL_STAGE_INPUT_MODE_FULLSCREEN, the stage absorbs all input.
|
||||
*
|
||||
* When the input mode is %SHELL_STAGE_INPUT_MODE_FOCUSED, the pointer
|
||||
* is handled as with %SHELL_STAGE_INPUT_MODE_NORMAL, but additionally
|
||||
* the stage window has the keyboard focus. If the stage loses the
|
||||
* focus (eg, because the user clicked into a window) the input mode
|
||||
* will revert to %SHELL_STAGE_INPUT_MODE_NORMAL.
|
||||
*
|
||||
* Note that whenever a mutter-internal Gtk widget has a pointer grab,
|
||||
* the shell goes unresponsive and passes things to the underlying GTK+
|
||||
* widget to ensure that the widget gets any clicks it is expecting.
|
||||
*/
|
||||
void
|
||||
shell_global_set_stage_input_mode (ShellGlobal *global,
|
||||
ShellStageInputMode mode)
|
||||
static ClutterActor *
|
||||
get_key_focused_actor (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen;
|
||||
ClutterActor *actor;
|
||||
|
||||
g_return_if_fail (SHELL_IS_GLOBAL (global));
|
||||
actor = clutter_stage_get_key_focus (global->stage);
|
||||
|
||||
screen = meta_plugin_get_screen (global->plugin);
|
||||
/* If there's no explicit key focus, clutter_stage_get_key_focus()
|
||||
* returns the stage. This is a terrible API. */
|
||||
if (actor == CLUTTER_ACTOR (global->stage))
|
||||
actor = NULL;
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
static void
|
||||
sync_stage_window_focus (ShellGlobal *global)
|
||||
{
|
||||
ClutterActor *actor;
|
||||
|
||||
if (global->has_modal)
|
||||
return;
|
||||
|
||||
actor = get_key_focused_actor (global);
|
||||
|
||||
/* An actor got key focus and the stage needs to be focused. */
|
||||
if (actor != NULL && !meta_stage_is_focused (global->meta_screen))
|
||||
meta_focus_stage_window (global->meta_screen,
|
||||
get_current_time_maybe_roundtrip (global));
|
||||
|
||||
/* An actor dropped key focus. Focus the default window. */
|
||||
else if (actor == NULL && meta_stage_is_focused (global->meta_screen))
|
||||
meta_screen_focus_default_window (global->meta_screen,
|
||||
get_current_time_maybe_roundtrip (global));
|
||||
}
|
||||
|
||||
static void
|
||||
focus_actor_changed (ClutterStage *stage,
|
||||
GParamSpec *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellGlobal *global = user_data;
|
||||
sync_stage_window_focus (global);
|
||||
}
|
||||
|
||||
static void
|
||||
sync_input_region (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen = global->meta_screen;
|
||||
|
||||
if (global->gtk_grab_active)
|
||||
meta_empty_stage_input_region (screen);
|
||||
else if (mode == SHELL_STAGE_INPUT_MODE_FULLSCREEN || !global->input_region)
|
||||
else if (global->has_modal)
|
||||
meta_set_stage_input_region (screen, None);
|
||||
else
|
||||
meta_set_stage_input_region (screen, global->input_region);
|
||||
|
||||
if (mode == SHELL_STAGE_INPUT_MODE_FOCUSED)
|
||||
meta_focus_stage_window (global->meta_screen,
|
||||
shell_global_get_current_time (global));
|
||||
|
||||
if (mode != global->input_mode)
|
||||
{
|
||||
global->input_mode = mode;
|
||||
g_object_notify (G_OBJECT (global), "stage-input-mode");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -682,8 +691,7 @@ shell_global_unset_cursor (ShellGlobal *global)
|
||||
* describing the input region.
|
||||
*
|
||||
* Sets the area of the stage that is responsive to mouse clicks when
|
||||
* the stage mode is %SHELL_STAGE_INPUT_MODE_NORMAL (but does not change the
|
||||
* current stage mode).
|
||||
* we don't have a modal or grab.
|
||||
*/
|
||||
void
|
||||
shell_global_set_stage_input_region (ShellGlobal *global,
|
||||
@ -713,10 +721,7 @@ shell_global_set_stage_input_region (ShellGlobal *global,
|
||||
global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects);
|
||||
g_free (rects);
|
||||
|
||||
/* set_stage_input_mode() will figure out whether or not we
|
||||
* should actually change the input region right now.
|
||||
*/
|
||||
shell_global_set_stage_input_mode (global, global->input_mode);
|
||||
sync_input_region (global);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -956,6 +961,8 @@ _shell_global_set_plugin (ShellGlobal *global,
|
||||
"End of stage page repaint",
|
||||
"");
|
||||
|
||||
g_signal_connect (global->stage, "notify::key-focus",
|
||||
G_CALLBACK (focus_actor_changed), global);
|
||||
g_signal_connect (global->meta_display, "notify::focus-window",
|
||||
G_CALLBACK (focus_window_changed), global);
|
||||
|
||||
@ -991,7 +998,14 @@ shell_global_begin_modal (ShellGlobal *global,
|
||||
guint32 timestamp,
|
||||
MetaModalOptions options)
|
||||
{
|
||||
return meta_plugin_begin_modal (global->plugin, global->stage_xwindow, None, options, timestamp);
|
||||
/* Make it an error to call begin_modal while we already
|
||||
* have a modal active. */
|
||||
if (global->has_modal)
|
||||
return FALSE;
|
||||
|
||||
global->has_modal = meta_plugin_begin_modal (global->plugin, global->stage_xwindow, None, options, timestamp);
|
||||
sync_input_region (global);
|
||||
return global->has_modal;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1004,7 +1018,25 @@ void
|
||||
shell_global_end_modal (ShellGlobal *global,
|
||||
guint32 timestamp)
|
||||
{
|
||||
ClutterActor *actor;
|
||||
|
||||
if (!global->has_modal)
|
||||
return;
|
||||
|
||||
meta_plugin_end_modal (global->plugin, timestamp);
|
||||
global->has_modal = FALSE;
|
||||
|
||||
/* If the stage window is unfocused, ensure that there's no
|
||||
* actor focused on Clutter's side. */
|
||||
if (!meta_stage_is_focused (global->meta_screen))
|
||||
clutter_stage_set_key_focus (global->stage, NULL);
|
||||
|
||||
/* An actor dropped key focus. Focus the default window. */
|
||||
else if (get_key_focused_actor (global) && meta_stage_is_focused (global->meta_screen))
|
||||
meta_screen_focus_default_window (global->meta_screen,
|
||||
get_current_time_maybe_roundtrip (global));
|
||||
|
||||
sync_input_region (global);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1222,7 +1254,7 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
|
||||
global->gtk_grab_active = !was_grabbed;
|
||||
|
||||
/* Update for the new setting of gtk_grab_active */
|
||||
shell_global_set_stage_input_mode (global, global->input_mode);
|
||||
sync_input_region (global);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1314,10 +1346,6 @@ shell_global_sync_pointer (ShellGlobal *global)
|
||||
event.type = CLUTTER_MOTION;
|
||||
event.time = shell_global_get_current_time (global);
|
||||
event.flags = 0;
|
||||
/* This is wrong: we should be setting event.stage to NULL if the
|
||||
* pointer is not inside the bounds of the stage given the current
|
||||
* stage_input_mode. For our current purposes however, this works.
|
||||
*/
|
||||
event.stage = global->stage;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
|
@ -47,14 +47,6 @@ void shell_global_end_modal (ShellGlobal *global,
|
||||
void shell_global_freeze_keyboard (ShellGlobal *global,
|
||||
guint32 timestamp);
|
||||
|
||||
typedef enum {
|
||||
SHELL_STAGE_INPUT_MODE_NORMAL,
|
||||
SHELL_STAGE_INPUT_MODE_FOCUSED,
|
||||
SHELL_STAGE_INPUT_MODE_FULLSCREEN
|
||||
} ShellStageInputMode;
|
||||
|
||||
void shell_global_set_stage_input_mode (ShellGlobal *global,
|
||||
ShellStageInputMode mode);
|
||||
void shell_global_set_stage_input_region (ShellGlobal *global,
|
||||
GSList *rectangles);
|
||||
|
||||
|
@ -43,8 +43,6 @@ struct _ShellNetworkAgentClass
|
||||
/* used by SHELL_TYPE_NETWORK_AGENT */
|
||||
GType shell_network_agent_get_type (void);
|
||||
|
||||
ShellNetworkAgent *shell_network_agent_new (void);
|
||||
|
||||
void shell_network_agent_set_password (ShellNetworkAgent *self,
|
||||
gchar *request_id,
|
||||
gchar *setting_key,
|
||||
|
@ -104,8 +104,8 @@ shell_slicer_paint_child (ShellSlicer *self)
|
||||
return;
|
||||
|
||||
st_bin_get_alignment (ST_BIN (self), &x_align, &y_align);
|
||||
_st_get_align_factors (x_align, y_align,
|
||||
&x_align_factor, &y_align_factor);
|
||||
st_get_align_factors (x_align, y_align,
|
||||
&x_align_factor, &y_align_factor);
|
||||
|
||||
clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &self_box);
|
||||
clutter_actor_get_allocation_box (child, &child_box);
|
||||
|
@ -108,8 +108,8 @@ st_bin_allocate (ClutterActor *self,
|
||||
gdouble x_align_f, y_align_f;
|
||||
|
||||
st_theme_node_get_content_box (theme_node, box, &childbox);
|
||||
_st_get_align_factors (priv->x_align, priv->y_align,
|
||||
&x_align_f, &y_align_f);
|
||||
st_get_align_factors (priv->x_align, priv->y_align,
|
||||
&x_align_f, &y_align_f);
|
||||
clutter_actor_allocate_align_fill (priv->child, &childbox,
|
||||
x_align_f, y_align_f,
|
||||
priv->x_fill, priv->y_fill,
|
||||
|
@ -285,7 +285,6 @@ get_content_preferred_width (StBoxLayout *self,
|
||||
{
|
||||
StBoxLayoutPrivate *priv = self->priv;
|
||||
gint n_children = 0;
|
||||
gint n_fixed = 0;
|
||||
gfloat min_width, natural_width;
|
||||
ClutterActor *child;
|
||||
|
||||
@ -304,12 +303,6 @@ get_content_preferred_width (StBoxLayout *self,
|
||||
|
||||
n_children++;
|
||||
|
||||
if (clutter_actor_get_fixed_position_set (child))
|
||||
{
|
||||
n_fixed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (priv->is_vertical)
|
||||
{
|
||||
_st_actor_get_preferred_width (child, -1, FALSE,
|
||||
@ -329,10 +322,10 @@ get_content_preferred_width (StBoxLayout *self,
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->is_vertical && (n_children - n_fixed) > 1)
|
||||
if (!priv->is_vertical && n_children > 1)
|
||||
{
|
||||
min_width += priv->spacing * (n_children - n_fixed - 1);
|
||||
natural_width += priv->spacing * (n_children - n_fixed - 1);
|
||||
min_width += priv->spacing * (n_children - 1);
|
||||
natural_width += priv->spacing * (n_children - 1);
|
||||
}
|
||||
|
||||
if (min_width_p)
|
||||
@ -367,7 +360,6 @@ get_content_preferred_height (StBoxLayout *self,
|
||||
{
|
||||
StBoxLayoutPrivate *priv = self->priv;
|
||||
gint n_children = 0;
|
||||
gint n_fixed = 0;
|
||||
gfloat min_height, natural_height;
|
||||
ClutterActor *child;
|
||||
|
||||
@ -386,12 +378,6 @@ get_content_preferred_height (StBoxLayout *self,
|
||||
|
||||
n_children++;
|
||||
|
||||
if (clutter_actor_get_fixed_position_set (child))
|
||||
{
|
||||
n_fixed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (priv->is_vertical)
|
||||
{
|
||||
clutter_container_child_get ((ClutterContainer*) self, child,
|
||||
@ -416,10 +402,10 @@ get_content_preferred_height (StBoxLayout *self,
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->is_vertical && (n_children - n_fixed) > 1)
|
||||
if (priv->is_vertical && n_children > 1)
|
||||
{
|
||||
min_height += priv->spacing * (n_children - n_fixed - 1);
|
||||
natural_height += priv->spacing * (n_children - n_fixed - 1);
|
||||
min_height += priv->spacing * (n_children - 1);
|
||||
natural_height += priv->spacing * (n_children - 1);
|
||||
}
|
||||
|
||||
if (min_height_p)
|
||||
@ -511,12 +497,9 @@ compute_shrinks (StBoxLayout *self,
|
||||
{
|
||||
gfloat child_min, child_nat;
|
||||
gboolean child_fill;
|
||||
gboolean fixed;
|
||||
|
||||
fixed = clutter_actor_get_fixed_position_set (child);
|
||||
|
||||
shrinks[i].child_index = i;
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (child) && !fixed)
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
{
|
||||
if (priv->is_vertical)
|
||||
{
|
||||
@ -738,20 +721,13 @@ st_box_layout_allocate (ClutterActor *actor,
|
||||
{
|
||||
ClutterActorBox child_box;
|
||||
gfloat child_min, child_nat, child_allocated;
|
||||
gboolean xfill, yfill, expand, fixed;
|
||||
gboolean xfill, yfill, expand;
|
||||
StAlign xalign, yalign;
|
||||
gdouble xalign_f, yalign_f;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
goto next_child;
|
||||
|
||||
fixed = clutter_actor_get_fixed_position_set (child);
|
||||
if (fixed)
|
||||
{
|
||||
clutter_actor_allocate_preferred_size (child, flags);
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
clutter_container_child_get ((ClutterContainer*) actor, child,
|
||||
"x-fill", &xfill,
|
||||
"y-fill", &yfill,
|
||||
@ -760,7 +736,7 @@ st_box_layout_allocate (ClutterActor *actor,
|
||||
"expand", &expand,
|
||||
NULL);
|
||||
|
||||
_st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f);
|
||||
st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f);
|
||||
|
||||
if (priv->is_vertical)
|
||||
{
|
||||
|
@ -98,66 +98,6 @@ _st_actor_get_preferred_height (ClutterActor *actor,
|
||||
clutter_actor_get_preferred_height (actor, for_width, min_height_p, natural_height_p);
|
||||
}
|
||||
|
||||
/**
|
||||
* _st_get_align_factors:
|
||||
* @x_align: an #StAlign
|
||||
* @y_align: an #StAlign
|
||||
* @x_align_out: (out) (allow-none): @x_align as a #gdouble
|
||||
* @y_align_out: (out) (allow-none): @y_align as a #gdouble
|
||||
*
|
||||
* Converts @x_align and @y_align to #gdouble values.
|
||||
*/
|
||||
void
|
||||
_st_get_align_factors (StAlign x_align,
|
||||
StAlign y_align,
|
||||
gdouble *x_align_out,
|
||||
gdouble *y_align_out)
|
||||
{
|
||||
if (x_align_out)
|
||||
{
|
||||
switch (x_align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
*x_align_out = 0.0;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_MIDDLE:
|
||||
*x_align_out = 0.5;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_END:
|
||||
*x_align_out = 1.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (y_align_out)
|
||||
{
|
||||
switch (y_align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
*y_align_out = 0.0;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_MIDDLE:
|
||||
*y_align_out = 0.5;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_END:
|
||||
*y_align_out = 1.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _st_set_text_from_style:
|
||||
* @text: Target #ClutterText
|
||||
|
@ -45,11 +45,6 @@ G_END_DECLS
|
||||
|
||||
ClutterActor *_st_widget_get_dnd_clone (StWidget *widget);
|
||||
|
||||
void _st_get_align_factors (StAlign x_align,
|
||||
StAlign y_align,
|
||||
gdouble *x_align_out,
|
||||
gdouble *y_align_out);
|
||||
|
||||
void _st_actor_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gboolean y_fill,
|
||||
|
@ -598,7 +598,7 @@ st_scroll_view_allocate (ClutterActor *actor,
|
||||
*/
|
||||
|
||||
/* Vertical scrollbar */
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
|
||||
if (vscrollbar_visible)
|
||||
{
|
||||
if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
|
||||
{
|
||||
@ -617,7 +617,7 @@ st_scroll_view_allocate (ClutterActor *actor,
|
||||
}
|
||||
|
||||
/* Horizontal scrollbar */
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
|
||||
if (hscrollbar_visible)
|
||||
{
|
||||
if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
|
||||
{
|
||||
|
@ -257,8 +257,8 @@ st_table_homogeneous_allocate (ClutterActor *self,
|
||||
row_span = meta->row_span;
|
||||
col_span = meta->col_span;
|
||||
|
||||
_st_get_align_factors (meta->x_align, meta->y_align,
|
||||
&x_align_f, &y_align_f);
|
||||
st_get_align_factors (meta->x_align, meta->y_align,
|
||||
&x_align_f, &y_align_f);
|
||||
|
||||
if (ltr)
|
||||
{
|
||||
@ -609,8 +609,8 @@ st_table_preferred_allocate (ClutterActor *self,
|
||||
row_span = meta->row_span;
|
||||
col_span = meta->col_span;
|
||||
|
||||
_st_get_align_factors (meta->x_align, meta->y_align,
|
||||
&x_align_f, &y_align_f);
|
||||
st_get_align_factors (meta->x_align, meta->y_align,
|
||||
&x_align_f, &y_align_f);
|
||||
|
||||
/* initialise the width and height */
|
||||
col_width = col_widths[col];
|
||||
|
@ -1268,8 +1268,7 @@ st_theme_node_prerender_background (StThemeNode *node,
|
||||
return texture;
|
||||
}
|
||||
|
||||
static void st_theme_node_paint_borders (StThemeNode *node,
|
||||
StThemeNodePaintState *state,
|
||||
static void st_theme_node_paint_borders (StThemeNodePaintState *state,
|
||||
const ClutterActorBox *box,
|
||||
guint8 paint_opacity);
|
||||
|
||||
@ -1370,9 +1369,11 @@ st_theme_node_load_background_image (StThemeNode *node)
|
||||
return node->background_texture != COGL_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
static void st_theme_node_prerender_shadow (StThemeNodePaintState *state);
|
||||
|
||||
static void
|
||||
st_theme_node_render_resources (StThemeNode *node,
|
||||
StThemeNodePaintState *state,
|
||||
st_theme_node_render_resources (StThemeNodePaintState *state,
|
||||
StThemeNode *node,
|
||||
float width,
|
||||
float height)
|
||||
{
|
||||
@ -1390,6 +1391,7 @@ st_theme_node_render_resources (StThemeNode *node,
|
||||
*/
|
||||
st_theme_node_paint_state_free (state);
|
||||
|
||||
st_theme_node_paint_state_set_node (state, node);
|
||||
state->alloc_width = width;
|
||||
state->alloc_height = height;
|
||||
|
||||
@ -1474,40 +1476,82 @@ st_theme_node_render_resources (StThemeNode *node,
|
||||
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
|
||||
state->prerendered_texture);
|
||||
else if (node->background_color.alpha > 0 || has_border)
|
||||
st_theme_node_prerender_shadow (state);
|
||||
}
|
||||
|
||||
/* If we don't have cached textures yet, check whether we can cache
|
||||
them. */
|
||||
if (!node->cached_textures)
|
||||
{
|
||||
if (state->prerendered_material == COGL_INVALID_HANDLE &&
|
||||
width >= node->box_shadow_min_width &&
|
||||
height >= node->box_shadow_min_height)
|
||||
{
|
||||
CoglHandle buffer, offscreen;
|
||||
int texture_width = ceil (width);
|
||||
int texture_height = ceil (height);
|
||||
|
||||
buffer = cogl_texture_new_with_size (texture_width,
|
||||
texture_height,
|
||||
COGL_TEXTURE_NO_SLICING,
|
||||
COGL_PIXEL_FORMAT_ANY);
|
||||
offscreen = cogl_offscreen_new_to_texture (buffer);
|
||||
|
||||
if (offscreen != COGL_INVALID_HANDLE)
|
||||
{
|
||||
ClutterActorBox box = { 0, 0, width, height };
|
||||
CoglColor clear_color;
|
||||
|
||||
cogl_push_framebuffer (offscreen);
|
||||
cogl_ortho (0, width, height, 0, 0, 1.0);
|
||||
|
||||
cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
|
||||
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
|
||||
|
||||
st_theme_node_paint_borders (node, state, &box, 0xFF);
|
||||
cogl_pop_framebuffer ();
|
||||
cogl_handle_unref (offscreen);
|
||||
|
||||
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
|
||||
buffer);
|
||||
}
|
||||
cogl_handle_unref (buffer);
|
||||
st_theme_node_paint_state_copy (&node->cached_state, state);
|
||||
node->cached_textures = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_update_resources (StThemeNodePaintState *state,
|
||||
StThemeNode *node,
|
||||
float width,
|
||||
float height)
|
||||
{
|
||||
gboolean had_prerendered_texture = FALSE;
|
||||
gboolean had_box_shadow = FALSE;
|
||||
StShadow *box_shadow_spec;
|
||||
|
||||
g_return_if_fail (width > 0 && height > 0);
|
||||
|
||||
/* Free handles we can't reuse */
|
||||
if (state->prerendered_texture != COGL_INVALID_HANDLE)
|
||||
{
|
||||
cogl_handle_unref (state->prerendered_texture);
|
||||
state->prerendered_texture = COGL_INVALID_HANDLE;
|
||||
had_prerendered_texture = TRUE;
|
||||
}
|
||||
if (state->prerendered_material != COGL_INVALID_HANDLE)
|
||||
{
|
||||
cogl_handle_unref (state->prerendered_material);
|
||||
state->prerendered_material = COGL_INVALID_HANDLE;
|
||||
|
||||
if (node->border_slices_texture == COGL_INVALID_HANDLE &&
|
||||
state->box_shadow_material != COGL_INVALID_HANDLE)
|
||||
{
|
||||
cogl_handle_unref (state->box_shadow_material);
|
||||
state->box_shadow_material = COGL_INVALID_HANDLE;
|
||||
had_box_shadow = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
st_theme_node_paint_state_set_node (state, node);
|
||||
state->alloc_width = width;
|
||||
state->alloc_height = height;
|
||||
|
||||
box_shadow_spec = st_theme_node_get_box_shadow (node);
|
||||
|
||||
if (had_prerendered_texture)
|
||||
{
|
||||
state->prerendered_texture = st_theme_node_prerender_background (node, width, height);
|
||||
state->prerendered_material = _st_create_texture_material (state->prerendered_texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
int corner_id;
|
||||
|
||||
for (corner_id = 0; corner_id < 4; corner_id++)
|
||||
if (state->corner_material[corner_id] == COGL_INVALID_HANDLE)
|
||||
state->corner_material[corner_id] =
|
||||
st_theme_node_lookup_corner (node, width, height, corner_id);
|
||||
}
|
||||
|
||||
if (had_box_shadow)
|
||||
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
|
||||
state->prerendered_texture);
|
||||
}
|
||||
|
||||
static void
|
||||
paint_material_with_opacity (CoglHandle material,
|
||||
ClutterActorBox *box,
|
||||
@ -1527,11 +1571,11 @@ paint_material_with_opacity (CoglHandle material,
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_paint_borders (StThemeNode *node,
|
||||
StThemeNodePaintState *state,
|
||||
st_theme_node_paint_borders (StThemeNodePaintState *state,
|
||||
const ClutterActorBox *box,
|
||||
guint8 paint_opacity)
|
||||
{
|
||||
StThemeNode *node = state->node;
|
||||
float width, height;
|
||||
int border_width[4];
|
||||
guint border_radius[4];
|
||||
@ -1792,6 +1836,353 @@ st_theme_node_paint_borders (StThemeNode *node,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_paint_sliced_shadow (StThemeNodePaintState *state,
|
||||
const ClutterActorBox *box,
|
||||
guint8 paint_opacity)
|
||||
{
|
||||
StThemeNode *node = state->node;
|
||||
guint border_radius[4];
|
||||
CoglColor color;
|
||||
StShadow *box_shadow_spec;
|
||||
gfloat xoffset, yoffset;
|
||||
gfloat width, height;
|
||||
gfloat shadow_width, shadow_height;
|
||||
gfloat xend, yend, top, bottom, left, right;
|
||||
gfloat s_top, s_bottom, s_left, s_right;
|
||||
gfloat shadow_blur_radius, x_spread_factor, y_spread_factor;
|
||||
float rectangles[8 * 9];
|
||||
gint idx;
|
||||
|
||||
if (paint_opacity == 0)
|
||||
return;
|
||||
|
||||
st_theme_node_reduce_border_radius (node, box->x2 - box->x1, box->y2 - box->y1, border_radius);
|
||||
|
||||
box_shadow_spec = st_theme_node_get_box_shadow (node);
|
||||
|
||||
/* Compute input & output areas :
|
||||
*
|
||||
* yoffset ----------------------------
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* top ----------------------------
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* bottom ----------------------------
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* yend ----------------------------
|
||||
* xoffset left right xend
|
||||
*
|
||||
* s_top = top in offscreen's coordinates (0.0 - 1.0)
|
||||
* s_bottom = bottom in offscreen's coordinates (0.0 - 1.0)
|
||||
* s_left = left in offscreen's coordinates (0.0 - 1.0)
|
||||
* s_right = right in offscreen's coordinates (0.0 - 1.0)
|
||||
*/
|
||||
if (box_shadow_spec->blur == 0)
|
||||
shadow_blur_radius = 0;
|
||||
else
|
||||
shadow_blur_radius = (5 * (box_shadow_spec->blur / 2.0)) / 2;
|
||||
|
||||
shadow_width = state->box_shadow_width + 2 * shadow_blur_radius;
|
||||
shadow_height = state->box_shadow_height + 2 * shadow_blur_radius;
|
||||
|
||||
/* Compute input regions parameters */
|
||||
s_top = shadow_blur_radius + box_shadow_spec->blur +
|
||||
MAX (node->border_radius[ST_CORNER_TOPLEFT],
|
||||
node->border_radius[ST_CORNER_TOPRIGHT]);
|
||||
s_bottom = shadow_blur_radius + box_shadow_spec->blur +
|
||||
MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
|
||||
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
|
||||
s_left = shadow_blur_radius + box_shadow_spec->blur +
|
||||
MAX (node->border_radius[ST_CORNER_TOPLEFT],
|
||||
node->border_radius[ST_CORNER_BOTTOMLEFT]);
|
||||
s_right = shadow_blur_radius + box_shadow_spec->blur +
|
||||
MAX (node->border_radius[ST_CORNER_TOPRIGHT],
|
||||
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
|
||||
|
||||
/* Compute output regions parameters */
|
||||
xoffset = box->x1 + box_shadow_spec->xoffset - shadow_blur_radius - box_shadow_spec->spread;
|
||||
yoffset = box->y1 + box_shadow_spec->yoffset - shadow_blur_radius - box_shadow_spec->spread;
|
||||
width = box->x2 - box->x1 + 2 * shadow_blur_radius;
|
||||
height = box->y2 - box->y1 + 2 * shadow_blur_radius;
|
||||
|
||||
x_spread_factor = (width + 2 * box_shadow_spec->spread) / width;
|
||||
y_spread_factor = (height + 2 * box_shadow_spec->spread) / height;
|
||||
|
||||
width += 2 * box_shadow_spec->spread;
|
||||
height += 2 * box_shadow_spec->spread;
|
||||
|
||||
xend = xoffset + width;
|
||||
yend = yoffset + height;
|
||||
|
||||
top = s_top * y_spread_factor;
|
||||
bottom = s_bottom * y_spread_factor;
|
||||
left = s_left * x_spread_factor;
|
||||
right = s_right * x_spread_factor;
|
||||
|
||||
bottom = height - bottom;
|
||||
right = width - right;
|
||||
|
||||
/* Final adjustments */
|
||||
s_top /= shadow_height;
|
||||
s_bottom /= shadow_height;
|
||||
s_left /= shadow_width;
|
||||
s_right /= shadow_width;
|
||||
|
||||
s_bottom = 1.0 - s_bottom;
|
||||
s_right = 1.0 - s_right;
|
||||
|
||||
top += yoffset;
|
||||
bottom += yoffset;
|
||||
left += xoffset;
|
||||
right += xoffset;
|
||||
|
||||
/* Setup pipeline */
|
||||
cogl_color_set_from_4ub (&color,
|
||||
box_shadow_spec->color.red * paint_opacity / 255,
|
||||
box_shadow_spec->color.green * paint_opacity / 255,
|
||||
box_shadow_spec->color.blue * paint_opacity / 255,
|
||||
box_shadow_spec->color.alpha * paint_opacity / 255);
|
||||
cogl_color_premultiply (&color);
|
||||
|
||||
cogl_material_set_layer_combine_constant (state->box_shadow_material, 0, &color);
|
||||
|
||||
cogl_set_source (state->box_shadow_material);
|
||||
|
||||
idx = 0;
|
||||
|
||||
if (top > 0)
|
||||
{
|
||||
if (left > 0)
|
||||
{
|
||||
/* Top left corner */
|
||||
rectangles[idx++] = xoffset;
|
||||
rectangles[idx++] = yoffset;
|
||||
rectangles[idx++] = left;
|
||||
rectangles[idx++] = top;
|
||||
|
||||
rectangles[idx++] = 0;
|
||||
rectangles[idx++] = 0;
|
||||
rectangles[idx++] = s_left;
|
||||
rectangles[idx++] = s_top;
|
||||
}
|
||||
|
||||
/* Top middle */
|
||||
rectangles[idx++] = left;
|
||||
rectangles[idx++] = yoffset;
|
||||
rectangles[idx++] = right;
|
||||
rectangles[idx++] = top;
|
||||
|
||||
rectangles[idx++] = s_left;
|
||||
rectangles[idx++] = 0;
|
||||
rectangles[idx++] = s_right;
|
||||
rectangles[idx++] = s_top;
|
||||
|
||||
if (right > 0)
|
||||
{
|
||||
/* Top right corner */
|
||||
rectangles[idx++] = right;
|
||||
rectangles[idx++] = yoffset;
|
||||
rectangles[idx++] = xend;
|
||||
rectangles[idx++] = top;
|
||||
|
||||
rectangles[idx++] = s_right;
|
||||
rectangles[idx++] = 0;
|
||||
rectangles[idx++] = 1;
|
||||
rectangles[idx++] = s_top;
|
||||
}
|
||||
}
|
||||
|
||||
if (left > 0)
|
||||
{
|
||||
/* Left middle */
|
||||
rectangles[idx++] = xoffset;
|
||||
rectangles[idx++] = top;
|
||||
rectangles[idx++] = left;
|
||||
rectangles[idx++] = bottom;
|
||||
|
||||
rectangles[idx++] = 0;
|
||||
rectangles[idx++] = s_top;
|
||||
rectangles[idx++] = s_left;
|
||||
rectangles[idx++] = s_bottom;
|
||||
}
|
||||
|
||||
/* Center middle */
|
||||
rectangles[idx++] = left;
|
||||
rectangles[idx++] = top;
|
||||
rectangles[idx++] = right;
|
||||
rectangles[idx++] = bottom;
|
||||
|
||||
rectangles[idx++] = s_left;
|
||||
rectangles[idx++] = s_top;
|
||||
rectangles[idx++] = s_right;
|
||||
rectangles[idx++] = s_bottom;
|
||||
|
||||
|
||||
if (right > 0)
|
||||
{
|
||||
/* Right middle */
|
||||
rectangles[idx++] = right;
|
||||
rectangles[idx++] = top;
|
||||
rectangles[idx++] = xend;
|
||||
rectangles[idx++] = bottom;
|
||||
|
||||
rectangles[idx++] = s_right;
|
||||
rectangles[idx++] = s_top;
|
||||
rectangles[idx++] = 1;
|
||||
rectangles[idx++] = s_bottom;
|
||||
}
|
||||
|
||||
if (bottom > 0)
|
||||
{
|
||||
if (left > 0)
|
||||
{
|
||||
/* Bottom left corner */
|
||||
rectangles[idx++] = xoffset;
|
||||
rectangles[idx++] = bottom;
|
||||
rectangles[idx++] = left;
|
||||
rectangles[idx++] = yend;
|
||||
|
||||
rectangles[idx++] = 0;
|
||||
rectangles[idx++] = s_bottom;
|
||||
rectangles[idx++] = s_left;
|
||||
rectangles[idx++] = 1;
|
||||
}
|
||||
|
||||
/* Bottom middle */
|
||||
rectangles[idx++] = left;
|
||||
rectangles[idx++] = bottom;
|
||||
rectangles[idx++] = right;
|
||||
rectangles[idx++] = yend;
|
||||
|
||||
rectangles[idx++] = s_left;
|
||||
rectangles[idx++] = s_bottom;
|
||||
rectangles[idx++] = s_right;
|
||||
rectangles[idx++] = 1;
|
||||
|
||||
if (right > 0)
|
||||
{
|
||||
/* Bottom right corner */
|
||||
rectangles[idx++] = right;
|
||||
rectangles[idx++] = bottom;
|
||||
rectangles[idx++] = xend;
|
||||
rectangles[idx++] = yend;
|
||||
|
||||
rectangles[idx++] = s_right;
|
||||
rectangles[idx++] = s_bottom;
|
||||
rectangles[idx++] = 1;
|
||||
rectangles[idx++] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
cogl_rectangles_with_texture_coords (rectangles, idx / 8);
|
||||
|
||||
#if 0
|
||||
/* Visual feedback on shadow's 9-slice and orignal offscreen buffer,
|
||||
for debug purposes */
|
||||
cogl_rectangle (xend, yoffset, xend + shadow_width, yoffset + shadow_height);
|
||||
|
||||
cogl_set_source_color4ub (0xff, 0x0, 0x0, 0xff);
|
||||
|
||||
cogl_rectangle (xoffset, top, xend, top + 1);
|
||||
cogl_rectangle (xoffset, bottom, xend, bottom + 1);
|
||||
cogl_rectangle (left, yoffset, left + 1, yend);
|
||||
cogl_rectangle (right, yoffset, right + 1, yend);
|
||||
|
||||
cogl_rectangle (xend, yoffset, xend + shadow_width, yoffset + 1);
|
||||
cogl_rectangle (xend, yoffset + shadow_height, xend + shadow_width, yoffset + shadow_height + 1);
|
||||
cogl_rectangle (xend, yoffset, xend + 1, yoffset + shadow_height);
|
||||
cogl_rectangle (xend + shadow_width, yoffset, xend + shadow_width + 1, yoffset + shadow_height);
|
||||
|
||||
s_top *= shadow_height;
|
||||
s_bottom *= shadow_height;
|
||||
s_left *= shadow_width;
|
||||
s_right *= shadow_width;
|
||||
|
||||
cogl_rectangle (xend, yoffset + s_top, xend + shadow_width, yoffset + s_top + 1);
|
||||
cogl_rectangle (xend, yoffset + s_bottom, xend + shadow_width, yoffset + s_bottom + 1);
|
||||
cogl_rectangle (xend + s_left, yoffset, xend + s_left + 1, yoffset + shadow_height);
|
||||
cogl_rectangle (xend + s_right, yoffset, xend + s_right + 1, yoffset + shadow_height);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_prerender_shadow (StThemeNodePaintState *state)
|
||||
{
|
||||
StThemeNode *node = state->node;
|
||||
guint border_radius[4];
|
||||
int max_borders[4];
|
||||
int center_radius, corner_id;
|
||||
CoglHandle buffer, offscreen;
|
||||
|
||||
/* Get infos from the node */
|
||||
if (state->alloc_width < node->box_shadow_min_width ||
|
||||
state->alloc_height < node->box_shadow_min_height)
|
||||
st_theme_node_reduce_border_radius (node, state->alloc_width, state->alloc_height, border_radius);
|
||||
else
|
||||
for (corner_id = 0; corner_id < 4; corner_id++)
|
||||
border_radius[corner_id] = node->border_radius[corner_id];
|
||||
|
||||
/* Compute maximum borders sizes */
|
||||
max_borders[ST_SIDE_TOP] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
|
||||
node->border_radius[ST_CORNER_TOPRIGHT]);
|
||||
max_borders[ST_SIDE_BOTTOM] = MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
|
||||
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
|
||||
max_borders[ST_SIDE_LEFT] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
|
||||
node->border_radius[ST_CORNER_BOTTOMLEFT]);
|
||||
max_borders[ST_SIDE_RIGHT] = MAX (node->border_radius[ST_CORNER_TOPRIGHT],
|
||||
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
|
||||
|
||||
center_radius = (node->box_shadow->blur > 0) ? (2 * node->box_shadow->blur + 1) : 1;
|
||||
node->box_shadow_min_width = max_borders[ST_SIDE_LEFT] + max_borders[ST_SIDE_RIGHT] + center_radius;
|
||||
node->box_shadow_min_height = max_borders[ST_SIDE_TOP] + max_borders[ST_SIDE_BOTTOM] + center_radius;
|
||||
|
||||
if (state->alloc_width < node->box_shadow_min_width ||
|
||||
state->alloc_height < node->box_shadow_min_height)
|
||||
{
|
||||
state->box_shadow_width = state->alloc_width;
|
||||
state->box_shadow_height = state->alloc_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->box_shadow_width = node->box_shadow_min_width;
|
||||
state->box_shadow_height = node->box_shadow_min_height;
|
||||
}
|
||||
|
||||
/* Render offscreen */
|
||||
buffer = cogl_texture_new_with_size (state->box_shadow_width,
|
||||
state->box_shadow_height,
|
||||
COGL_TEXTURE_NO_SLICING,
|
||||
COGL_PIXEL_FORMAT_ANY);
|
||||
offscreen = cogl_offscreen_new_to_texture (buffer);
|
||||
|
||||
if (offscreen != COGL_INVALID_HANDLE)
|
||||
{
|
||||
ClutterActorBox box = { 0, 0, state->box_shadow_width, state->box_shadow_height};
|
||||
CoglColor clear_color;
|
||||
|
||||
cogl_push_framebuffer (offscreen);
|
||||
cogl_ortho (0, state->box_shadow_width, state->box_shadow_height, 0, 0, 1.0);
|
||||
|
||||
cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
|
||||
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
|
||||
|
||||
st_theme_node_paint_borders (state, &box, 0xFF);
|
||||
cogl_pop_framebuffer ();
|
||||
cogl_handle_unref (offscreen);
|
||||
|
||||
state->box_shadow_material = _st_create_shadow_material (st_theme_node_get_box_shadow (node),
|
||||
buffer);
|
||||
}
|
||||
cogl_handle_unref (buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_paint_sliced_border_image (StThemeNode *node,
|
||||
float width,
|
||||
@ -1945,6 +2336,43 @@ st_theme_node_paint_outline (StThemeNode *node,
|
||||
cogl_rectangles (rects, 4);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_theme_node_needs_new_box_shadow_for_size (StThemeNodePaintState *state,
|
||||
StThemeNode *node,
|
||||
float width,
|
||||
float height)
|
||||
{
|
||||
if (!node->rendered_once)
|
||||
return TRUE;
|
||||
|
||||
/* The allocation hasn't changed, no need to recompute a new
|
||||
box-shadow. */
|
||||
if (state->alloc_width == width &&
|
||||
state->alloc_height == height)
|
||||
return FALSE;
|
||||
|
||||
/* If there is no shadow, no need to recompute a new box-shadow. */
|
||||
if (node->box_shadow_min_width == 0 ||
|
||||
node->box_shadow_min_height == 0)
|
||||
return FALSE;
|
||||
|
||||
/* If the new size is inferior to the box-shadow minimum size (we
|
||||
already know the size has changed), we need to recompute the
|
||||
box-shadow. */
|
||||
if (width < node->box_shadow_min_width ||
|
||||
height < node->box_shadow_min_height)
|
||||
return TRUE;
|
||||
|
||||
/* Now checking whether the size of the node has crossed the minimum
|
||||
box-shadow size boundary, from below to above the minimum size .
|
||||
If that's the case, we need to recompute the box-shadow */
|
||||
if (state->alloc_width < node->box_shadow_min_width ||
|
||||
state->alloc_height < node->box_shadow_min_height)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_node_paint (StThemeNode *node,
|
||||
StThemeNodePaintState *state,
|
||||
@ -1964,11 +2392,28 @@ st_theme_node_paint (StThemeNode *node,
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
if (state->alloc_width != width || state->alloc_height != height)
|
||||
/* Check whether we need to recreate the textures of the paint
|
||||
* state, either because :
|
||||
* 1) the theme node associated to the paint state has changed
|
||||
* 2) the allocation size change requires recreating textures
|
||||
*/
|
||||
if (state->node != node ||
|
||||
st_theme_node_needs_new_box_shadow_for_size (state, node, width, height))
|
||||
{
|
||||
state->node = node;
|
||||
st_theme_node_render_resources (node, state, width, height);
|
||||
/* If we had the ability to cache textures on the node, then we
|
||||
can just copy them over to the paint state and avoid all
|
||||
rendering. We end up sharing textures a cross different
|
||||
widgets. */
|
||||
if (node->rendered_once && node->cached_textures &&
|
||||
width >= node->box_shadow_min_width && height >= node->box_shadow_min_height)
|
||||
st_theme_node_paint_state_copy (state, &node->cached_state);
|
||||
else
|
||||
st_theme_node_render_resources (state, node, width, height);
|
||||
|
||||
node->rendered_once = TRUE;
|
||||
}
|
||||
else if (state->alloc_width != width || state->alloc_height != height)
|
||||
st_theme_node_update_resources (state, node, width, height);
|
||||
|
||||
/* Rough notes about the relationship of borders and backgrounds in CSS3;
|
||||
* see http://www.w3.org/TR/css3-background/ for more accurate details.
|
||||
@ -1997,10 +2442,18 @@ st_theme_node_paint (StThemeNode *node,
|
||||
*/
|
||||
|
||||
if (state->box_shadow_material)
|
||||
_st_paint_shadow_with_opacity (node->box_shadow,
|
||||
state->box_shadow_material,
|
||||
&allocation,
|
||||
paint_opacity);
|
||||
{
|
||||
if (state->alloc_width < node->box_shadow_min_width ||
|
||||
state->alloc_height < node->box_shadow_min_height)
|
||||
_st_paint_shadow_with_opacity (node->box_shadow,
|
||||
state->box_shadow_material,
|
||||
&allocation,
|
||||
paint_opacity);
|
||||
else
|
||||
st_theme_node_paint_sliced_shadow (state,
|
||||
&allocation,
|
||||
paint_opacity);
|
||||
}
|
||||
|
||||
if (state->prerendered_material != COGL_INVALID_HANDLE ||
|
||||
st_theme_node_load_border_image (node))
|
||||
@ -2024,7 +2477,7 @@ st_theme_node_paint (StThemeNode *node,
|
||||
}
|
||||
else
|
||||
{
|
||||
st_theme_node_paint_borders (node, state, box, paint_opacity);
|
||||
st_theme_node_paint_borders (state, box, paint_opacity);
|
||||
}
|
||||
|
||||
st_theme_node_paint_outline (node, box, paint_opacity);
|
||||
@ -2076,8 +2529,9 @@ st_theme_node_paint (StThemeNode *node,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_node_paint_state_free (StThemeNodePaintState *state)
|
||||
static void
|
||||
st_theme_node_paint_state_node_free_internal (StThemeNodePaintState *state,
|
||||
gboolean unref_node)
|
||||
{
|
||||
int corner_id;
|
||||
|
||||
@ -2092,14 +2546,46 @@ st_theme_node_paint_state_free (StThemeNodePaintState *state)
|
||||
if (state->corner_material[corner_id] != COGL_INVALID_HANDLE)
|
||||
cogl_handle_unref (state->corner_material[corner_id]);
|
||||
|
||||
if (unref_node)
|
||||
st_theme_node_paint_state_set_node (state, NULL);
|
||||
|
||||
st_theme_node_paint_state_init (state);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_paint_state_node_freed (StThemeNodePaintState *state)
|
||||
{
|
||||
st_theme_node_paint_state_node_free_internal (state, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_node_paint_state_set_node (StThemeNodePaintState *state, StThemeNode *node)
|
||||
{
|
||||
if (state->node)
|
||||
g_object_weak_unref (G_OBJECT (state->node),
|
||||
(GWeakNotify) st_theme_node_paint_state_node_freed,
|
||||
state);
|
||||
|
||||
state->node = node;
|
||||
if (state->node)
|
||||
g_object_weak_ref (G_OBJECT (state->node),
|
||||
(GWeakNotify) st_theme_node_paint_state_node_freed,
|
||||
state);
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_node_paint_state_free (StThemeNodePaintState *state)
|
||||
{
|
||||
st_theme_node_paint_state_node_free_internal (state, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_node_paint_state_init (StThemeNodePaintState *state)
|
||||
{
|
||||
int corner_id;
|
||||
|
||||
state->alloc_width = 0;
|
||||
state->alloc_height = 0;
|
||||
state->node = NULL;
|
||||
state->box_shadow_material = COGL_INVALID_HANDLE;
|
||||
state->prerendered_texture = COGL_INVALID_HANDLE;
|
||||
@ -2120,10 +2606,12 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state,
|
||||
|
||||
st_theme_node_paint_state_free (state);
|
||||
|
||||
state->node = other->node;
|
||||
st_theme_node_paint_state_set_node (state, other->node);
|
||||
|
||||
state->alloc_width = other->alloc_width;
|
||||
state->alloc_height = other->alloc_height;
|
||||
state->box_shadow_width = other->box_shadow_width;
|
||||
state->box_shadow_height = other->box_shadow_height;
|
||||
|
||||
if (other->box_shadow_material)
|
||||
state->box_shadow_material = cogl_handle_ref (other->box_shadow_material);
|
||||
|
@ -99,12 +99,19 @@ struct _StThemeNode {
|
||||
guint background_image_shadow_computed : 1;
|
||||
guint text_shadow_computed : 1;
|
||||
guint link_type : 2;
|
||||
guint rendered_once : 1;
|
||||
guint cached_textures : 1;
|
||||
|
||||
int box_shadow_min_width;
|
||||
int box_shadow_min_height;
|
||||
|
||||
CoglHandle border_slices_texture;
|
||||
CoglHandle border_slices_material;
|
||||
CoglHandle background_texture;
|
||||
CoglHandle background_material;
|
||||
CoglHandle background_shadow_material;
|
||||
|
||||
StThemeNodePaintState cached_state;
|
||||
};
|
||||
|
||||
struct _StThemeNodeClass {
|
||||
|
@ -148,9 +148,13 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
|
||||
if (st_theme_node_equal (new_node, old_node))
|
||||
{
|
||||
{
|
||||
StThemeNodePaintState tmp = priv->old_paint_state;
|
||||
priv->old_paint_state = priv->new_paint_state;
|
||||
priv->new_paint_state = tmp;
|
||||
StThemeNodePaintState tmp;
|
||||
|
||||
st_theme_node_paint_state_init (&tmp);
|
||||
st_theme_node_paint_state_copy (&tmp, &priv->old_paint_state);
|
||||
st_theme_node_paint_state_copy (&priv->old_paint_state, &priv->new_paint_state);
|
||||
st_theme_node_paint_state_copy (&priv->new_paint_state, &tmp);
|
||||
st_theme_node_paint_state_free (&tmp);
|
||||
}
|
||||
|
||||
if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)
|
||||
|
@ -54,6 +54,8 @@ st_theme_node_init (StThemeNode *node)
|
||||
node->background_shadow_material = COGL_INVALID_HANDLE;
|
||||
node->border_slices_texture = COGL_INVALID_HANDLE;
|
||||
node->border_slices_material = COGL_INVALID_HANDLE;
|
||||
|
||||
st_theme_node_paint_state_init (&node->cached_state);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -101,6 +103,8 @@ st_theme_node_dispose (GObject *gobject)
|
||||
g_signal_handlers_disconnect_by_func (node->theme,
|
||||
on_custom_stylesheets_changed, node);
|
||||
|
||||
st_theme_node_paint_state_free (&node->cached_state);
|
||||
|
||||
g_clear_object (&node->theme);
|
||||
|
||||
G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject);
|
||||
@ -3056,6 +3060,10 @@ parse_shadow_property (StThemeNode *node,
|
||||
*/
|
||||
for (term = decl->value; term; term = term->next)
|
||||
{
|
||||
/* if we found "none", we're all set with the default values */
|
||||
if (term_is_none (term))
|
||||
return VALUE_FOUND;
|
||||
|
||||
if (term->type == TERM_NUMBER)
|
||||
{
|
||||
gdouble value;
|
||||
|
@ -102,6 +102,9 @@ struct _StThemeNodePaintState {
|
||||
float alloc_width;
|
||||
float alloc_height;
|
||||
|
||||
float box_shadow_width;
|
||||
float box_shadow_height;
|
||||
|
||||
CoglHandle box_shadow_material;
|
||||
CoglHandle prerendered_texture;
|
||||
CoglHandle prerendered_material;
|
||||
@ -278,6 +281,8 @@ void st_theme_node_paint_state_free (StThemeNodePaintState *state);
|
||||
void st_theme_node_paint_state_copy (StThemeNodePaintState *state,
|
||||
StThemeNodePaintState *other);
|
||||
void st_theme_node_paint_state_invalidate (StThemeNodePaintState *state);
|
||||
void st_theme_node_paint_state_set_node (StThemeNodePaintState *state,
|
||||
StThemeNode *node);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -67,6 +67,8 @@ struct _StWidgetPrivate
|
||||
gboolean hover : 1;
|
||||
gboolean can_focus : 1;
|
||||
|
||||
gulong texture_file_changed_id;
|
||||
|
||||
AtkObject *accessible;
|
||||
AtkRole accessible_role;
|
||||
AtkStateSet *local_state_set;
|
||||
@ -362,9 +364,11 @@ st_widget_dispose (GObject *gobject)
|
||||
priv->label_actor = NULL;
|
||||
}
|
||||
|
||||
g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
|
||||
st_widget_texture_cache_changed,
|
||||
actor);
|
||||
if (priv->texture_file_changed_id != 0)
|
||||
{
|
||||
g_signal_handler_disconnect (st_texture_cache_get_default (), priv->texture_file_changed_id);
|
||||
priv->texture_file_changed_id = 0;
|
||||
}
|
||||
|
||||
g_clear_object (&priv->prev_first_child);
|
||||
g_clear_object (&priv->prev_last_child);
|
||||
@ -385,7 +389,7 @@ st_widget_finalize (GObject *gobject)
|
||||
g_free (priv->inline_style);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
|
||||
st_theme_node_paint_state_init (&priv->paint_states[i]);
|
||||
st_theme_node_paint_state_free (&priv->paint_states[i]);
|
||||
|
||||
G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
|
||||
}
|
||||
@ -643,8 +647,9 @@ st_widget_get_theme_node (StWidget *widget)
|
||||
|
||||
if (stage == NULL)
|
||||
{
|
||||
g_error ("st_widget_get_theme_node called on the widget %s which is not in the stage.",
|
||||
st_describe_actor (CLUTTER_ACTOR (widget)));
|
||||
g_critical ("st_widget_get_theme_node called on the widget %s which is not in the stage.",
|
||||
st_describe_actor (CLUTTER_ACTOR (widget)));
|
||||
return g_object_new (ST_TYPE_THEME_NODE, NULL);
|
||||
}
|
||||
|
||||
if (parent_node == NULL)
|
||||
@ -1551,8 +1556,8 @@ st_widget_init (StWidget *actor)
|
||||
|
||||
g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL);
|
||||
g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL);
|
||||
g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
|
||||
G_CALLBACK (st_widget_texture_cache_changed), actor);
|
||||
priv->texture_file_changed_id = g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
|
||||
G_CALLBACK (st_widget_texture_cache_changed), actor);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
|
||||
st_theme_node_paint_state_init (&priv->paint_states[i]);
|
||||
@ -2880,3 +2885,63 @@ st_widget_get_focus_chain (StWidget *widget)
|
||||
{
|
||||
return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_get_align_factors:
|
||||
* @x_align: an #StAlign
|
||||
* @y_align: an #StAlign
|
||||
* @x_align_out: (out) (allow-none): @x_align as a #gdouble
|
||||
* @y_align_out: (out) (allow-none): @y_align as a #gdouble
|
||||
*
|
||||
* Converts @x_align and @y_align to #gdouble values.
|
||||
*/
|
||||
void
|
||||
st_get_align_factors (StAlign x_align,
|
||||
StAlign y_align,
|
||||
gdouble *x_align_out,
|
||||
gdouble *y_align_out)
|
||||
{
|
||||
if (x_align_out)
|
||||
{
|
||||
switch (x_align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
*x_align_out = 0.0;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_MIDDLE:
|
||||
*x_align_out = 0.5;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_END:
|
||||
*x_align_out = 1.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (y_align_out)
|
||||
{
|
||||
switch (y_align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
*y_align_out = 0.0;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_MIDDLE:
|
||||
*y_align_out = 0.5;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_END:
|
||||
*y_align_out = 1.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +151,6 @@ StThemeNode * st_widget_peek_theme_node (StWidget *widg
|
||||
GList * st_widget_get_focus_chain (StWidget *widget);
|
||||
void st_widget_paint_background (StWidget *widget);
|
||||
|
||||
|
||||
/* debug methods */
|
||||
char *st_describe_actor (ClutterActor *actor);
|
||||
void st_set_slow_down_factor (gfloat factor);
|
||||
@ -169,6 +168,12 @@ void st_widget_set_accessible_name (StWidget *widget,
|
||||
const gchar *name);
|
||||
const gchar * st_widget_get_accessible_name (StWidget *widget);
|
||||
|
||||
/* utility methods */
|
||||
void st_get_align_factors (StAlign x_align,
|
||||
StAlign y_align,
|
||||
gdouble *x_align_out,
|
||||
gdouble *y_align_out);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_WIDGET_H__ */
|
||||
|
@ -8,6 +8,7 @@ TEST_JS = \
|
||||
interactive/border-radius.js \
|
||||
interactive/border-width.js \
|
||||
interactive/box-layout.js \
|
||||
interactive/box-shadow-animated.js \
|
||||
interactive/box-shadows.js \
|
||||
interactive/calendar.js \
|
||||
interactive/css-fonts.js \
|
||||
|
@ -45,12 +45,6 @@ function test() {
|
||||
style: 'border: 1px solid #aaaaaa; '
|
||||
+ 'background: #cceeff' }));
|
||||
|
||||
b2.add(new St.Label({ x: 50,
|
||||
y: 50,
|
||||
text: "Fixed",
|
||||
style: 'border: 1px solid #aaaaaa;'
|
||||
+ 'background: #ffffcc' }));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function createCollapsableBox(width) {
|
||||
|
84
tests/interactive/box-shadow-animated.js
Normal file
84
tests/interactive/box-shadow-animated.js
Normal file
@ -0,0 +1,84 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const UI = imports.testcommon.ui;
|
||||
|
||||
const DELAY = 2000;
|
||||
|
||||
function resize_animated(label) {
|
||||
if (label.width == 100) {
|
||||
label.save_easing_state();
|
||||
label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
|
||||
label.set_easing_duration(DELAY - 50);
|
||||
label.set_size(500, 500);
|
||||
label.restore_easing_state();
|
||||
} else {
|
||||
label.save_easing_state();
|
||||
label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
|
||||
label.set_easing_duration(DELAY - 50);
|
||||
label.set_size(100, 100);
|
||||
label.restore_easing_state();
|
||||
}
|
||||
}
|
||||
|
||||
function get_css_style(shadow_style)
|
||||
{
|
||||
return 'border: 20px solid black;' +
|
||||
'border-radius: 20px;' +
|
||||
'background-color: white; ' +
|
||||
'padding: 5px;' + shadow_style;
|
||||
}
|
||||
|
||||
function test() {
|
||||
let stage = new Clutter.Stage({ width: 1000, height: 600 });
|
||||
UI.init(stage);
|
||||
|
||||
let iter = 0;
|
||||
let shadowStyles = [ 'box-shadow: 3px 50px 0px 4px rgba(0,0,0,0.5);',
|
||||
'box-shadow: 3px 4px 10px 4px rgba(0,0,0,0.5);',
|
||||
'box-shadow: 0px 50px 0px 0px rgba(0,0,0,0.5);',
|
||||
'box-shadow: 100px 100px 20px 4px rgba(0,0,0,0.5);'];
|
||||
let label1 = new St.Label({ style: get_css_style(shadowStyles[iter]),
|
||||
text: shadowStyles[iter],
|
||||
x: 20,
|
||||
y: 20,
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
stage.add_actor(label1);
|
||||
let label2 = new St.Label({ style: get_css_style(shadowStyles[iter]),
|
||||
text: shadowStyles[iter],
|
||||
x: 500,
|
||||
y: 20,
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
stage.add_actor(label2);
|
||||
|
||||
resize_animated(label1);
|
||||
resize_animated(label2);
|
||||
Mainloop.timeout_add(DELAY, Lang.bind(this, function() {
|
||||
log(label1 + label1.get_size());
|
||||
resize_animated(label1);
|
||||
resize_animated(label2);
|
||||
return true;
|
||||
}));
|
||||
|
||||
Mainloop.timeout_add(2 * DELAY, Lang.bind(this, function() {
|
||||
iter += 1;
|
||||
iter %= shadowStyles.length;
|
||||
label1.set_style(get_css_style(shadowStyles[iter]));
|
||||
label1.set_text(shadowStyles[iter]);
|
||||
label2.set_style(get_css_style(shadowStyles[iter]));
|
||||
label2.set_text(shadowStyles[iter]);
|
||||
return true;
|
||||
}));
|
||||
|
||||
UI.main(stage);
|
||||
}
|
||||
test();
|
Reference in New Issue
Block a user